xref: /aosp_15_r20/external/pytorch/tools/setup_helpers/cmake.py (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
1*da0073e9SAndroid Build Coastguard Worker"Manages CMake."
2*da0073e9SAndroid Build Coastguard Worker
3*da0073e9SAndroid Build Coastguard Workerfrom __future__ import annotations
4*da0073e9SAndroid Build Coastguard Worker
5*da0073e9SAndroid Build Coastguard Workerimport multiprocessing
6*da0073e9SAndroid Build Coastguard Workerimport os
7*da0073e9SAndroid Build Coastguard Workerimport platform
8*da0073e9SAndroid Build Coastguard Workerimport sys
9*da0073e9SAndroid Build Coastguard Workerimport sysconfig
10*da0073e9SAndroid Build Coastguard Workerfrom distutils.version import LooseVersion
11*da0073e9SAndroid Build Coastguard Workerfrom subprocess import CalledProcessError, check_call, check_output
12*da0073e9SAndroid Build Coastguard Workerfrom typing import Any, cast
13*da0073e9SAndroid Build Coastguard Worker
14*da0073e9SAndroid Build Coastguard Workerfrom . import which
15*da0073e9SAndroid Build Coastguard Workerfrom .cmake_utils import CMakeValue, get_cmake_cache_variables_from_file
16*da0073e9SAndroid Build Coastguard Workerfrom .env import BUILD_DIR, check_negative_env_flag, IS_64BIT, IS_DARWIN, IS_WINDOWS
17*da0073e9SAndroid Build Coastguard Worker
18*da0073e9SAndroid Build Coastguard Worker
19*da0073e9SAndroid Build Coastguard Workerdef _mkdir_p(d: str) -> None:
20*da0073e9SAndroid Build Coastguard Worker    try:
21*da0073e9SAndroid Build Coastguard Worker        os.makedirs(d, exist_ok=True)
22*da0073e9SAndroid Build Coastguard Worker    except OSError as e:
23*da0073e9SAndroid Build Coastguard Worker        raise RuntimeError(
24*da0073e9SAndroid Build Coastguard Worker            f"Failed to create folder {os.path.abspath(d)}: {e.strerror}"
25*da0073e9SAndroid Build Coastguard Worker        ) from e
26*da0073e9SAndroid Build Coastguard Worker
27*da0073e9SAndroid Build Coastguard Worker
28*da0073e9SAndroid Build Coastguard Worker# Ninja
29*da0073e9SAndroid Build Coastguard Worker# Use ninja if it is on the PATH. Previous version of PyTorch required the
30*da0073e9SAndroid Build Coastguard Worker# ninja python package, but we no longer use it, so we do not have to import it
31*da0073e9SAndroid Build Coastguard WorkerUSE_NINJA = not check_negative_env_flag("USE_NINJA") and which("ninja") is not None
32*da0073e9SAndroid Build Coastguard Workerif "CMAKE_GENERATOR" in os.environ:
33*da0073e9SAndroid Build Coastguard Worker    USE_NINJA = os.environ["CMAKE_GENERATOR"].lower() == "ninja"
34*da0073e9SAndroid Build Coastguard Worker
35*da0073e9SAndroid Build Coastguard Worker
36*da0073e9SAndroid Build Coastguard Workerclass CMake:
37*da0073e9SAndroid Build Coastguard Worker    "Manages cmake."
38*da0073e9SAndroid Build Coastguard Worker
39*da0073e9SAndroid Build Coastguard Worker    def __init__(self, build_dir: str = BUILD_DIR) -> None:
40*da0073e9SAndroid Build Coastguard Worker        self._cmake_command = CMake._get_cmake_command()
41*da0073e9SAndroid Build Coastguard Worker        self.build_dir = build_dir
42*da0073e9SAndroid Build Coastguard Worker
43*da0073e9SAndroid Build Coastguard Worker    @property
44*da0073e9SAndroid Build Coastguard Worker    def _cmake_cache_file(self) -> str:
45*da0073e9SAndroid Build Coastguard Worker        r"""Returns the path to CMakeCache.txt.
46*da0073e9SAndroid Build Coastguard Worker
47*da0073e9SAndroid Build Coastguard Worker        Returns:
48*da0073e9SAndroid Build Coastguard Worker          string: The path to CMakeCache.txt.
49*da0073e9SAndroid Build Coastguard Worker        """
50*da0073e9SAndroid Build Coastguard Worker        return os.path.join(self.build_dir, "CMakeCache.txt")
51*da0073e9SAndroid Build Coastguard Worker
52*da0073e9SAndroid Build Coastguard Worker    @staticmethod
53*da0073e9SAndroid Build Coastguard Worker    def _get_cmake_command() -> str:
54*da0073e9SAndroid Build Coastguard Worker        "Returns cmake command."
55*da0073e9SAndroid Build Coastguard Worker
56*da0073e9SAndroid Build Coastguard Worker        cmake_command = "cmake"
57*da0073e9SAndroid Build Coastguard Worker        if IS_WINDOWS:
58*da0073e9SAndroid Build Coastguard Worker            return cmake_command
59*da0073e9SAndroid Build Coastguard Worker        cmake3_version = CMake._get_version(which("cmake3"))
60*da0073e9SAndroid Build Coastguard Worker        cmake_version = CMake._get_version(which("cmake"))
61*da0073e9SAndroid Build Coastguard Worker
62*da0073e9SAndroid Build Coastguard Worker        _cmake_min_version = LooseVersion("3.18.0")
63*da0073e9SAndroid Build Coastguard Worker        if all(
64*da0073e9SAndroid Build Coastguard Worker            ver is None or ver < _cmake_min_version
65*da0073e9SAndroid Build Coastguard Worker            for ver in [cmake_version, cmake3_version]
66*da0073e9SAndroid Build Coastguard Worker        ):
67*da0073e9SAndroid Build Coastguard Worker            raise RuntimeError("no cmake or cmake3 with version >= 3.18.0 found")
68*da0073e9SAndroid Build Coastguard Worker
69*da0073e9SAndroid Build Coastguard Worker        if cmake3_version is None:
70*da0073e9SAndroid Build Coastguard Worker            cmake_command = "cmake"
71*da0073e9SAndroid Build Coastguard Worker        elif cmake_version is None:
72*da0073e9SAndroid Build Coastguard Worker            cmake_command = "cmake3"
73*da0073e9SAndroid Build Coastguard Worker        else:
74*da0073e9SAndroid Build Coastguard Worker            if cmake3_version >= cmake_version:
75*da0073e9SAndroid Build Coastguard Worker                cmake_command = "cmake3"
76*da0073e9SAndroid Build Coastguard Worker            else:
77*da0073e9SAndroid Build Coastguard Worker                cmake_command = "cmake"
78*da0073e9SAndroid Build Coastguard Worker        return cmake_command
79*da0073e9SAndroid Build Coastguard Worker
80*da0073e9SAndroid Build Coastguard Worker    @staticmethod
81*da0073e9SAndroid Build Coastguard Worker    def _get_version(cmd: str | None) -> Any:
82*da0073e9SAndroid Build Coastguard Worker        "Returns cmake version."
83*da0073e9SAndroid Build Coastguard Worker
84*da0073e9SAndroid Build Coastguard Worker        if cmd is None:
85*da0073e9SAndroid Build Coastguard Worker            return None
86*da0073e9SAndroid Build Coastguard Worker        for line in check_output([cmd, "--version"]).decode("utf-8").split("\n"):
87*da0073e9SAndroid Build Coastguard Worker            if "version" in line:
88*da0073e9SAndroid Build Coastguard Worker                return LooseVersion(line.strip().split(" ")[2])
89*da0073e9SAndroid Build Coastguard Worker        raise RuntimeError("no version found")
90*da0073e9SAndroid Build Coastguard Worker
91*da0073e9SAndroid Build Coastguard Worker    def run(self, args: list[str], env: dict[str, str]) -> None:
92*da0073e9SAndroid Build Coastguard Worker        "Executes cmake with arguments and an environment."
93*da0073e9SAndroid Build Coastguard Worker
94*da0073e9SAndroid Build Coastguard Worker        command = [self._cmake_command] + args
95*da0073e9SAndroid Build Coastguard Worker        print(" ".join(command))
96*da0073e9SAndroid Build Coastguard Worker        try:
97*da0073e9SAndroid Build Coastguard Worker            check_call(command, cwd=self.build_dir, env=env)
98*da0073e9SAndroid Build Coastguard Worker        except (CalledProcessError, KeyboardInterrupt) as e:
99*da0073e9SAndroid Build Coastguard Worker            # This error indicates that there was a problem with cmake, the
100*da0073e9SAndroid Build Coastguard Worker            # Python backtrace adds no signal here so skip over it by catching
101*da0073e9SAndroid Build Coastguard Worker            # the error and exiting manually
102*da0073e9SAndroid Build Coastguard Worker            sys.exit(1)
103*da0073e9SAndroid Build Coastguard Worker
104*da0073e9SAndroid Build Coastguard Worker    @staticmethod
105*da0073e9SAndroid Build Coastguard Worker    def defines(args: list[str], **kwargs: CMakeValue) -> None:
106*da0073e9SAndroid Build Coastguard Worker        "Adds definitions to a cmake argument list."
107*da0073e9SAndroid Build Coastguard Worker        for key, value in sorted(kwargs.items()):
108*da0073e9SAndroid Build Coastguard Worker            if value is not None:
109*da0073e9SAndroid Build Coastguard Worker                args.append(f"-D{key}={value}")
110*da0073e9SAndroid Build Coastguard Worker
111*da0073e9SAndroid Build Coastguard Worker    def get_cmake_cache_variables(self) -> dict[str, CMakeValue]:
112*da0073e9SAndroid Build Coastguard Worker        r"""Gets values in CMakeCache.txt into a dictionary.
113*da0073e9SAndroid Build Coastguard Worker        Returns:
114*da0073e9SAndroid Build Coastguard Worker          dict: A ``dict`` containing the value of cached CMake variables.
115*da0073e9SAndroid Build Coastguard Worker        """
116*da0073e9SAndroid Build Coastguard Worker        with open(self._cmake_cache_file) as f:
117*da0073e9SAndroid Build Coastguard Worker            return get_cmake_cache_variables_from_file(f)
118*da0073e9SAndroid Build Coastguard Worker
119*da0073e9SAndroid Build Coastguard Worker    def generate(
120*da0073e9SAndroid Build Coastguard Worker        self,
121*da0073e9SAndroid Build Coastguard Worker        version: str | None,
122*da0073e9SAndroid Build Coastguard Worker        cmake_python_library: str | None,
123*da0073e9SAndroid Build Coastguard Worker        build_python: bool,
124*da0073e9SAndroid Build Coastguard Worker        build_test: bool,
125*da0073e9SAndroid Build Coastguard Worker        my_env: dict[str, str],
126*da0073e9SAndroid Build Coastguard Worker        rerun: bool,
127*da0073e9SAndroid Build Coastguard Worker    ) -> None:
128*da0073e9SAndroid Build Coastguard Worker        "Runs cmake to generate native build files."
129*da0073e9SAndroid Build Coastguard Worker
130*da0073e9SAndroid Build Coastguard Worker        if rerun and os.path.isfile(self._cmake_cache_file):
131*da0073e9SAndroid Build Coastguard Worker            os.remove(self._cmake_cache_file)
132*da0073e9SAndroid Build Coastguard Worker
133*da0073e9SAndroid Build Coastguard Worker        ninja_build_file = os.path.join(self.build_dir, "build.ninja")
134*da0073e9SAndroid Build Coastguard Worker        if os.path.exists(self._cmake_cache_file) and not (
135*da0073e9SAndroid Build Coastguard Worker            USE_NINJA and not os.path.exists(ninja_build_file)
136*da0073e9SAndroid Build Coastguard Worker        ):
137*da0073e9SAndroid Build Coastguard Worker            # Everything's in place. Do not rerun.
138*da0073e9SAndroid Build Coastguard Worker            return
139*da0073e9SAndroid Build Coastguard Worker
140*da0073e9SAndroid Build Coastguard Worker        args = []
141*da0073e9SAndroid Build Coastguard Worker        if USE_NINJA:
142*da0073e9SAndroid Build Coastguard Worker            # Avoid conflicts in '-G' and the `CMAKE_GENERATOR`
143*da0073e9SAndroid Build Coastguard Worker            os.environ["CMAKE_GENERATOR"] = "Ninja"
144*da0073e9SAndroid Build Coastguard Worker            args.append("-GNinja")
145*da0073e9SAndroid Build Coastguard Worker        elif IS_WINDOWS:
146*da0073e9SAndroid Build Coastguard Worker            generator = os.getenv("CMAKE_GENERATOR", "Visual Studio 16 2019")
147*da0073e9SAndroid Build Coastguard Worker            supported = ["Visual Studio 16 2019", "Visual Studio 17 2022"]
148*da0073e9SAndroid Build Coastguard Worker            if generator not in supported:
149*da0073e9SAndroid Build Coastguard Worker                print("Unsupported `CMAKE_GENERATOR`: " + generator)
150*da0073e9SAndroid Build Coastguard Worker                print("Please set it to one of the following values: ")
151*da0073e9SAndroid Build Coastguard Worker                print("\n".join(supported))
152*da0073e9SAndroid Build Coastguard Worker                sys.exit(1)
153*da0073e9SAndroid Build Coastguard Worker            args.append("-G" + generator)
154*da0073e9SAndroid Build Coastguard Worker            toolset_dict = {}
155*da0073e9SAndroid Build Coastguard Worker            toolset_version = os.getenv("CMAKE_GENERATOR_TOOLSET_VERSION")
156*da0073e9SAndroid Build Coastguard Worker            if toolset_version is not None:
157*da0073e9SAndroid Build Coastguard Worker                toolset_dict["version"] = toolset_version
158*da0073e9SAndroid Build Coastguard Worker                curr_toolset = os.getenv("VCToolsVersion")
159*da0073e9SAndroid Build Coastguard Worker                if curr_toolset is None:
160*da0073e9SAndroid Build Coastguard Worker                    print(
161*da0073e9SAndroid Build Coastguard Worker                        "When you specify `CMAKE_GENERATOR_TOOLSET_VERSION`, you must also "
162*da0073e9SAndroid Build Coastguard Worker                        "activate the vs environment of this version. Please read the notes "
163*da0073e9SAndroid Build Coastguard Worker                        "in the build steps carefully."
164*da0073e9SAndroid Build Coastguard Worker                    )
165*da0073e9SAndroid Build Coastguard Worker                    sys.exit(1)
166*da0073e9SAndroid Build Coastguard Worker            if IS_64BIT:
167*da0073e9SAndroid Build Coastguard Worker                if platform.machine() == "ARM64":
168*da0073e9SAndroid Build Coastguard Worker                    args.append("-A ARM64")
169*da0073e9SAndroid Build Coastguard Worker                else:
170*da0073e9SAndroid Build Coastguard Worker                    args.append("-Ax64")
171*da0073e9SAndroid Build Coastguard Worker                    toolset_dict["host"] = "x64"
172*da0073e9SAndroid Build Coastguard Worker            if toolset_dict:
173*da0073e9SAndroid Build Coastguard Worker                toolset_expr = ",".join([f"{k}={v}" for k, v in toolset_dict.items()])
174*da0073e9SAndroid Build Coastguard Worker                args.append("-T" + toolset_expr)
175*da0073e9SAndroid Build Coastguard Worker
176*da0073e9SAndroid Build Coastguard Worker        base_dir = os.path.dirname(
177*da0073e9SAndroid Build Coastguard Worker            os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
178*da0073e9SAndroid Build Coastguard Worker        )
179*da0073e9SAndroid Build Coastguard Worker        install_dir = os.path.join(base_dir, "torch")
180*da0073e9SAndroid Build Coastguard Worker
181*da0073e9SAndroid Build Coastguard Worker        _mkdir_p(install_dir)
182*da0073e9SAndroid Build Coastguard Worker        _mkdir_p(self.build_dir)
183*da0073e9SAndroid Build Coastguard Worker
184*da0073e9SAndroid Build Coastguard Worker        # Store build options that are directly stored in environment variables
185*da0073e9SAndroid Build Coastguard Worker        build_options: dict[str, CMakeValue] = {}
186*da0073e9SAndroid Build Coastguard Worker
187*da0073e9SAndroid Build Coastguard Worker        # Build options that do not start with "BUILD_", "USE_", or "CMAKE_" and are directly controlled by env vars.
188*da0073e9SAndroid Build Coastguard Worker        # This is a dict that maps environment variables to the corresponding variable name in CMake.
189*da0073e9SAndroid Build Coastguard Worker        additional_options = {
190*da0073e9SAndroid Build Coastguard Worker            # Key: environment variable name. Value: Corresponding variable name to be passed to CMake. If you are
191*da0073e9SAndroid Build Coastguard Worker            # adding a new build option to this block: Consider making these two names identical and adding this option
192*da0073e9SAndroid Build Coastguard Worker            # in the block below.
193*da0073e9SAndroid Build Coastguard Worker            "_GLIBCXX_USE_CXX11_ABI": "GLIBCXX_USE_CXX11_ABI",
194*da0073e9SAndroid Build Coastguard Worker            "CUDNN_LIB_DIR": "CUDNN_LIBRARY",
195*da0073e9SAndroid Build Coastguard Worker            "USE_CUDA_STATIC_LINK": "CAFFE2_STATIC_LINK_CUDA",
196*da0073e9SAndroid Build Coastguard Worker        }
197*da0073e9SAndroid Build Coastguard Worker        additional_options.update(
198*da0073e9SAndroid Build Coastguard Worker            {
199*da0073e9SAndroid Build Coastguard Worker                # Build options that have the same environment variable name and CMake variable name and that do not start
200*da0073e9SAndroid Build Coastguard Worker                # with "BUILD_", "USE_", or "CMAKE_". If you are adding a new build option, also make sure you add it to
201*da0073e9SAndroid Build Coastguard Worker                # CMakeLists.txt.
202*da0073e9SAndroid Build Coastguard Worker                var: var
203*da0073e9SAndroid Build Coastguard Worker                for var in (
204*da0073e9SAndroid Build Coastguard Worker                    "UBSAN_FLAGS",
205*da0073e9SAndroid Build Coastguard Worker                    "BLAS",
206*da0073e9SAndroid Build Coastguard Worker                    "WITH_BLAS",
207*da0073e9SAndroid Build Coastguard Worker                    "CUDA_HOST_COMPILER",
208*da0073e9SAndroid Build Coastguard Worker                    "CUDA_NVCC_EXECUTABLE",
209*da0073e9SAndroid Build Coastguard Worker                    "CUDA_SEPARABLE_COMPILATION",
210*da0073e9SAndroid Build Coastguard Worker                    "CUDNN_LIBRARY",
211*da0073e9SAndroid Build Coastguard Worker                    "CUDNN_INCLUDE_DIR",
212*da0073e9SAndroid Build Coastguard Worker                    "CUDNN_ROOT",
213*da0073e9SAndroid Build Coastguard Worker                    "EXPERIMENTAL_SINGLE_THREAD_POOL",
214*da0073e9SAndroid Build Coastguard Worker                    "INSTALL_TEST",
215*da0073e9SAndroid Build Coastguard Worker                    "JAVA_HOME",
216*da0073e9SAndroid Build Coastguard Worker                    "INTEL_MKL_DIR",
217*da0073e9SAndroid Build Coastguard Worker                    "INTEL_OMP_DIR",
218*da0073e9SAndroid Build Coastguard Worker                    "MKL_THREADING",
219*da0073e9SAndroid Build Coastguard Worker                    "MKLDNN_CPU_RUNTIME",
220*da0073e9SAndroid Build Coastguard Worker                    "MSVC_Z7_OVERRIDE",
221*da0073e9SAndroid Build Coastguard Worker                    "CAFFE2_USE_MSVC_STATIC_RUNTIME",
222*da0073e9SAndroid Build Coastguard Worker                    "Numa_INCLUDE_DIR",
223*da0073e9SAndroid Build Coastguard Worker                    "Numa_LIBRARIES",
224*da0073e9SAndroid Build Coastguard Worker                    "ONNX_ML",
225*da0073e9SAndroid Build Coastguard Worker                    "ONNX_NAMESPACE",
226*da0073e9SAndroid Build Coastguard Worker                    "ATEN_THREADING",
227*da0073e9SAndroid Build Coastguard Worker                    "WERROR",
228*da0073e9SAndroid Build Coastguard Worker                    "OPENSSL_ROOT_DIR",
229*da0073e9SAndroid Build Coastguard Worker                    "STATIC_DISPATCH_BACKEND",
230*da0073e9SAndroid Build Coastguard Worker                    "SELECTED_OP_LIST",
231*da0073e9SAndroid Build Coastguard Worker                    "TORCH_CUDA_ARCH_LIST",
232*da0073e9SAndroid Build Coastguard Worker                    "TRACING_BASED",
233*da0073e9SAndroid Build Coastguard Worker                    "PYTHON_LIB_REL_PATH",
234*da0073e9SAndroid Build Coastguard Worker                )
235*da0073e9SAndroid Build Coastguard Worker            }
236*da0073e9SAndroid Build Coastguard Worker        )
237*da0073e9SAndroid Build Coastguard Worker
238*da0073e9SAndroid Build Coastguard Worker        # Aliases which are lower priority than their canonical option
239*da0073e9SAndroid Build Coastguard Worker        low_priority_aliases = {
240*da0073e9SAndroid Build Coastguard Worker            "CUDA_HOST_COMPILER": "CMAKE_CUDA_HOST_COMPILER",
241*da0073e9SAndroid Build Coastguard Worker            "CUDAHOSTCXX": "CUDA_HOST_COMPILER",
242*da0073e9SAndroid Build Coastguard Worker            "CMAKE_CUDA_HOST_COMPILER": "CUDA_HOST_COMPILER",
243*da0073e9SAndroid Build Coastguard Worker            "CMAKE_CUDA_COMPILER": "CUDA_NVCC_EXECUTABLE",
244*da0073e9SAndroid Build Coastguard Worker            "CUDACXX": "CUDA_NVCC_EXECUTABLE",
245*da0073e9SAndroid Build Coastguard Worker        }
246*da0073e9SAndroid Build Coastguard Worker        for var, val in my_env.items():
247*da0073e9SAndroid Build Coastguard Worker            # We currently pass over all environment variables that start with "BUILD_", "USE_", and "CMAKE_". This is
248*da0073e9SAndroid Build Coastguard Worker            # because we currently have no reliable way to get the list of all build options we have specified in
249*da0073e9SAndroid Build Coastguard Worker            # CMakeLists.txt. (`cmake -L` won't print dependent options when the dependency condition is not met.) We
250*da0073e9SAndroid Build Coastguard Worker            # will possibly change this in the future by parsing CMakeLists.txt ourselves (then additional_options would
251*da0073e9SAndroid Build Coastguard Worker            # also not be needed to be specified here).
252*da0073e9SAndroid Build Coastguard Worker            true_var = additional_options.get(var)
253*da0073e9SAndroid Build Coastguard Worker            if true_var is not None:
254*da0073e9SAndroid Build Coastguard Worker                build_options[true_var] = val
255*da0073e9SAndroid Build Coastguard Worker            elif var.startswith(("BUILD_", "USE_", "CMAKE_")) or var.endswith(
256*da0073e9SAndroid Build Coastguard Worker                ("EXITCODE", "EXITCODE__TRYRUN_OUTPUT")
257*da0073e9SAndroid Build Coastguard Worker            ):
258*da0073e9SAndroid Build Coastguard Worker                build_options[var] = val
259*da0073e9SAndroid Build Coastguard Worker
260*da0073e9SAndroid Build Coastguard Worker            if var in low_priority_aliases:
261*da0073e9SAndroid Build Coastguard Worker                key = low_priority_aliases[var]
262*da0073e9SAndroid Build Coastguard Worker                if key not in build_options:
263*da0073e9SAndroid Build Coastguard Worker                    build_options[key] = val
264*da0073e9SAndroid Build Coastguard Worker
265*da0073e9SAndroid Build Coastguard Worker        # The default value cannot be easily obtained in CMakeLists.txt. We set it here.
266*da0073e9SAndroid Build Coastguard Worker        py_lib_path = sysconfig.get_path("purelib")
267*da0073e9SAndroid Build Coastguard Worker        cmake_prefix_path = build_options.get("CMAKE_PREFIX_PATH", None)
268*da0073e9SAndroid Build Coastguard Worker        if cmake_prefix_path:
269*da0073e9SAndroid Build Coastguard Worker            build_options["CMAKE_PREFIX_PATH"] = (
270*da0073e9SAndroid Build Coastguard Worker                py_lib_path + ";" + cast(str, cmake_prefix_path)
271*da0073e9SAndroid Build Coastguard Worker            )
272*da0073e9SAndroid Build Coastguard Worker        else:
273*da0073e9SAndroid Build Coastguard Worker            build_options["CMAKE_PREFIX_PATH"] = py_lib_path
274*da0073e9SAndroid Build Coastguard Worker
275*da0073e9SAndroid Build Coastguard Worker        # Some options must be post-processed. Ideally, this list will be shrunk to only one or two options in the
276*da0073e9SAndroid Build Coastguard Worker        # future, as CMake can detect many of these libraries pretty comfortably. We have them here for now before CMake
277*da0073e9SAndroid Build Coastguard Worker        # integration is completed. They appear here not in the CMake.defines call below because they start with either
278*da0073e9SAndroid Build Coastguard Worker        # "BUILD_" or "USE_" and must be overwritten here.
279*da0073e9SAndroid Build Coastguard Worker        build_options.update(
280*da0073e9SAndroid Build Coastguard Worker            {
281*da0073e9SAndroid Build Coastguard Worker                # Note: Do not add new build options to this dict if it is directly read from environment variable -- you
282*da0073e9SAndroid Build Coastguard Worker                # only need to add one in `CMakeLists.txt`. All build options that start with "BUILD_", "USE_", or "CMAKE_"
283*da0073e9SAndroid Build Coastguard Worker                # are automatically passed to CMake; For other options you can add to additional_options above.
284*da0073e9SAndroid Build Coastguard Worker                "BUILD_PYTHON": build_python,
285*da0073e9SAndroid Build Coastguard Worker                "BUILD_TEST": build_test,
286*da0073e9SAndroid Build Coastguard Worker                # Most library detection should go to CMake script, except this one, which Python can do a much better job
287*da0073e9SAndroid Build Coastguard Worker                # due to NumPy's inherent Pythonic nature.
288*da0073e9SAndroid Build Coastguard Worker                "USE_NUMPY": not check_negative_env_flag("USE_NUMPY"),
289*da0073e9SAndroid Build Coastguard Worker            }
290*da0073e9SAndroid Build Coastguard Worker        )
291*da0073e9SAndroid Build Coastguard Worker
292*da0073e9SAndroid Build Coastguard Worker        # Options starting with CMAKE_
293*da0073e9SAndroid Build Coastguard Worker        cmake__options = {
294*da0073e9SAndroid Build Coastguard Worker            "CMAKE_INSTALL_PREFIX": install_dir,
295*da0073e9SAndroid Build Coastguard Worker        }
296*da0073e9SAndroid Build Coastguard Worker
297*da0073e9SAndroid Build Coastguard Worker        # We set some CMAKE_* options in our Python build code instead of relying on the user's direct settings. Emit an
298*da0073e9SAndroid Build Coastguard Worker        # error if the user also attempts to set these CMAKE options directly.
299*da0073e9SAndroid Build Coastguard Worker        specified_cmake__options = set(build_options).intersection(cmake__options)
300*da0073e9SAndroid Build Coastguard Worker        if len(specified_cmake__options) > 0:
301*da0073e9SAndroid Build Coastguard Worker            print(
302*da0073e9SAndroid Build Coastguard Worker                ", ".join(specified_cmake__options)
303*da0073e9SAndroid Build Coastguard Worker                + " should not be specified in the environment variable. They are directly set by PyTorch build script."
304*da0073e9SAndroid Build Coastguard Worker            )
305*da0073e9SAndroid Build Coastguard Worker            sys.exit(1)
306*da0073e9SAndroid Build Coastguard Worker        build_options.update(cmake__options)
307*da0073e9SAndroid Build Coastguard Worker
308*da0073e9SAndroid Build Coastguard Worker        CMake.defines(
309*da0073e9SAndroid Build Coastguard Worker            args,
310*da0073e9SAndroid Build Coastguard Worker            Python_EXECUTABLE=sys.executable,
311*da0073e9SAndroid Build Coastguard Worker            TORCH_BUILD_VERSION=version,
312*da0073e9SAndroid Build Coastguard Worker            **build_options,
313*da0073e9SAndroid Build Coastguard Worker        )
314*da0073e9SAndroid Build Coastguard Worker
315*da0073e9SAndroid Build Coastguard Worker        expected_wrapper = "/usr/local/opt/ccache/libexec"
316*da0073e9SAndroid Build Coastguard Worker        if IS_DARWIN and os.path.exists(expected_wrapper):
317*da0073e9SAndroid Build Coastguard Worker            if "CMAKE_C_COMPILER" not in build_options and "CC" not in os.environ:
318*da0073e9SAndroid Build Coastguard Worker                CMake.defines(args, CMAKE_C_COMPILER=f"{expected_wrapper}/gcc")
319*da0073e9SAndroid Build Coastguard Worker            if "CMAKE_CXX_COMPILER" not in build_options and "CXX" not in os.environ:
320*da0073e9SAndroid Build Coastguard Worker                CMake.defines(args, CMAKE_CXX_COMPILER=f"{expected_wrapper}/g++")
321*da0073e9SAndroid Build Coastguard Worker
322*da0073e9SAndroid Build Coastguard Worker        for env_var_name in my_env:
323*da0073e9SAndroid Build Coastguard Worker            if env_var_name.startswith("gh"):
324*da0073e9SAndroid Build Coastguard Worker                # github env vars use utf-8, on windows, non-ascii code may
325*da0073e9SAndroid Build Coastguard Worker                # cause problem, so encode first
326*da0073e9SAndroid Build Coastguard Worker                try:
327*da0073e9SAndroid Build Coastguard Worker                    my_env[env_var_name] = str(my_env[env_var_name].encode("utf-8"))
328*da0073e9SAndroid Build Coastguard Worker                except UnicodeDecodeError as e:
329*da0073e9SAndroid Build Coastguard Worker                    shex = ":".join(f"{ord(c):02x}" for c in my_env[env_var_name])
330*da0073e9SAndroid Build Coastguard Worker                    print(
331*da0073e9SAndroid Build Coastguard Worker                        f"Invalid ENV[{env_var_name}] = {shex}",
332*da0073e9SAndroid Build Coastguard Worker                        file=sys.stderr,
333*da0073e9SAndroid Build Coastguard Worker                    )
334*da0073e9SAndroid Build Coastguard Worker                    print(e, file=sys.stderr)
335*da0073e9SAndroid Build Coastguard Worker        # According to the CMake manual, we should pass the arguments first,
336*da0073e9SAndroid Build Coastguard Worker        # and put the directory as the last element. Otherwise, these flags
337*da0073e9SAndroid Build Coastguard Worker        # may not be passed correctly.
338*da0073e9SAndroid Build Coastguard Worker        # Reference:
339*da0073e9SAndroid Build Coastguard Worker        # 1. https://cmake.org/cmake/help/latest/manual/cmake.1.html#synopsis
340*da0073e9SAndroid Build Coastguard Worker        # 2. https://stackoverflow.com/a/27169347
341*da0073e9SAndroid Build Coastguard Worker        args.append(base_dir)
342*da0073e9SAndroid Build Coastguard Worker        self.run(args, env=my_env)
343*da0073e9SAndroid Build Coastguard Worker
344*da0073e9SAndroid Build Coastguard Worker    def build(self, my_env: dict[str, str]) -> None:
345*da0073e9SAndroid Build Coastguard Worker        "Runs cmake to build binaries."
346*da0073e9SAndroid Build Coastguard Worker
347*da0073e9SAndroid Build Coastguard Worker        from .env import build_type
348*da0073e9SAndroid Build Coastguard Worker
349*da0073e9SAndroid Build Coastguard Worker        build_args = [
350*da0073e9SAndroid Build Coastguard Worker            "--build",
351*da0073e9SAndroid Build Coastguard Worker            ".",
352*da0073e9SAndroid Build Coastguard Worker            "--target",
353*da0073e9SAndroid Build Coastguard Worker            "install",
354*da0073e9SAndroid Build Coastguard Worker            "--config",
355*da0073e9SAndroid Build Coastguard Worker            build_type.build_type_string,
356*da0073e9SAndroid Build Coastguard Worker        ]
357*da0073e9SAndroid Build Coastguard Worker
358*da0073e9SAndroid Build Coastguard Worker        # Determine the parallelism according to the following
359*da0073e9SAndroid Build Coastguard Worker        # priorities:
360*da0073e9SAndroid Build Coastguard Worker        # 1) MAX_JOBS environment variable
361*da0073e9SAndroid Build Coastguard Worker        # 2) If using the Ninja build system, delegate decision to it.
362*da0073e9SAndroid Build Coastguard Worker        # 3) Otherwise, fall back to the number of processors.
363*da0073e9SAndroid Build Coastguard Worker
364*da0073e9SAndroid Build Coastguard Worker        # Allow the user to set parallelism explicitly. If unset,
365*da0073e9SAndroid Build Coastguard Worker        # we'll try to figure it out.
366*da0073e9SAndroid Build Coastguard Worker        max_jobs = os.getenv("MAX_JOBS")
367*da0073e9SAndroid Build Coastguard Worker
368*da0073e9SAndroid Build Coastguard Worker        if max_jobs is not None or not USE_NINJA:
369*da0073e9SAndroid Build Coastguard Worker            # Ninja is capable of figuring out the parallelism on its
370*da0073e9SAndroid Build Coastguard Worker            # own: only specify it explicitly if we are not using
371*da0073e9SAndroid Build Coastguard Worker            # Ninja.
372*da0073e9SAndroid Build Coastguard Worker
373*da0073e9SAndroid Build Coastguard Worker            # This lists the number of processors available on the
374*da0073e9SAndroid Build Coastguard Worker            # machine. This may be an overestimate of the usable
375*da0073e9SAndroid Build Coastguard Worker            # processors if CPU scheduling affinity limits it
376*da0073e9SAndroid Build Coastguard Worker            # further. In the future, we should check for that with
377*da0073e9SAndroid Build Coastguard Worker            # os.sched_getaffinity(0) on platforms that support it.
378*da0073e9SAndroid Build Coastguard Worker            max_jobs = max_jobs or str(multiprocessing.cpu_count())
379*da0073e9SAndroid Build Coastguard Worker
380*da0073e9SAndroid Build Coastguard Worker            # This ``if-else'' clause would be unnecessary when cmake
381*da0073e9SAndroid Build Coastguard Worker            # 3.12 becomes minimum, which provides a '-j' option:
382*da0073e9SAndroid Build Coastguard Worker            # build_args += ['-j', max_jobs] would be sufficient by
383*da0073e9SAndroid Build Coastguard Worker            # then. Until then, we use "--" to pass parameters to the
384*da0073e9SAndroid Build Coastguard Worker            # underlying build system.
385*da0073e9SAndroid Build Coastguard Worker            build_args += ["--"]
386*da0073e9SAndroid Build Coastguard Worker            if IS_WINDOWS and not USE_NINJA:
387*da0073e9SAndroid Build Coastguard Worker                # We are likely using msbuild here
388*da0073e9SAndroid Build Coastguard Worker                build_args += [f"/p:CL_MPCount={max_jobs}"]
389*da0073e9SAndroid Build Coastguard Worker            else:
390*da0073e9SAndroid Build Coastguard Worker                build_args += ["-j", max_jobs]
391*da0073e9SAndroid Build Coastguard Worker        self.run(build_args, my_env)
392