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