Skip to content

Commit 15cbe64

Browse files
committed
[build] remove cmake cache and reconfigure again if it is invalid
ghstack-source-id: 967655e Pull-Request: pytorch#156958
1 parent d0612bf commit 15cbe64

File tree

2 files changed

+67
-24
lines changed

2 files changed

+67
-24
lines changed

tools/setup_helpers/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22

33
import os
44
import sys
5+
import warnings
56

67

78
def which(thefile: str) -> str | None:
9+
warnings.warn(
10+
"tools.setup_helpers.which is deprecated and will be removed in a future version. "
11+
"Use shutil.which instead.",
12+
FutureWarning,
13+
stacklevel=2,
14+
)
15+
816
path = os.environ.get("PATH", os.defpath).split(os.pathsep)
917
for d in path:
1018
fname = os.path.join(d, thefile)

tools/setup_helpers/cmake.py

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
"Manages CMake."
1+
"""Manages CMake."""
22

33
from __future__ import annotations
44

5+
import functools
56
import json
67
import multiprocessing
78
import os
89
import platform
10+
import shutil
911
import sys
1012
import sysconfig
1113
from pathlib import Path
1214
from subprocess import CalledProcessError, check_call, check_output, DEVNULL
1315
from typing import cast
1416

15-
from . import which
1617
from .cmake_utils import CMakeValue, get_cmake_cache_variables_from_file
1718
from .env import BUILD_DIR, check_negative_env_flag, IS_64BIT, IS_DARWIN, IS_WINDOWS
1819

@@ -37,10 +38,14 @@ def _mkdir_p(d: str) -> None:
3738
) from e
3839

3940

41+
# Print to stderr
42+
eprint = functools.partial(print, file=sys.stderr, flush=True)
43+
44+
4045
# Ninja
4146
# Use ninja if it is on the PATH. Previous version of PyTorch required the
4247
# ninja python package, but we no longer use it, so we do not have to import it
43-
USE_NINJA = not check_negative_env_flag("USE_NINJA") and which("ninja") is not None
48+
USE_NINJA = bool(not check_negative_env_flag("USE_NINJA") and shutil.which("ninja"))
4449
if "CMAKE_GENERATOR" in os.environ:
4550
USE_NINJA = os.environ["CMAKE_GENERATOR"].lower() == "ninja"
4651

@@ -61,15 +66,24 @@ def _cmake_cache_file(self) -> str:
6166
"""
6267
return os.path.join(self.build_dir, "CMakeCache.txt")
6368

69+
@property
70+
def _ninja_build_file(self) -> str:
71+
r"""Returns the path to build.ninja.
72+
73+
Returns:
74+
string: The path to build.ninja.
75+
"""
76+
return os.path.join(self.build_dir, "build.ninja")
77+
6478
@staticmethod
6579
def _get_cmake_command() -> str:
66-
"Returns cmake command."
80+
"""Returns cmake command."""
6781

6882
cmake_command = "cmake"
6983
if IS_WINDOWS:
7084
return cmake_command
71-
cmake3_version = CMake._get_version(which("cmake3"))
72-
cmake_version = CMake._get_version(which("cmake"))
85+
cmake3_version = CMake._get_version(shutil.which("cmake3"))
86+
cmake_version = CMake._get_version(shutil.which("cmake"))
7387

7488
_cmake_min_version = Version("3.27.0")
7589
if all(
@@ -115,10 +129,10 @@ def _get_version(cmd: str | None) -> Version | None:
115129
raise RuntimeError(f"Failed to get CMake version from command: {cmd}")
116130

117131
def run(self, args: list[str], env: dict[str, str]) -> None:
118-
"Executes cmake with arguments and an environment."
132+
"""Executes cmake with arguments and an environment."""
119133

120134
command = [self._cmake_command] + args
121-
print(" ".join(command))
135+
eprint(" ".join(command))
122136
try:
123137
check_call(command, cwd=self.build_dir, env=env)
124138
except (CalledProcessError, KeyboardInterrupt):
@@ -129,7 +143,7 @@ def run(self, args: list[str], env: dict[str, str]) -> None:
129143

130144
@staticmethod
131145
def defines(args: list[str], **kwargs: CMakeValue) -> None:
132-
"Adds definitions to a cmake argument list."
146+
"""Adds definitions to a cmake argument list."""
133147
for key, value in sorted(kwargs.items()):
134148
if value is not None:
135149
args.append(f"-D{key}={value}")
@@ -151,14 +165,31 @@ def generate(
151165
my_env: dict[str, str],
152166
rerun: bool,
153167
) -> None:
154-
"Runs cmake to generate native build files."
168+
"""Runs cmake to generate native build files."""
155169

156170
if rerun and os.path.isfile(self._cmake_cache_file):
157171
os.remove(self._cmake_cache_file)
158172

159-
ninja_build_file = os.path.join(self.build_dir, "build.ninja")
160-
if os.path.exists(self._cmake_cache_file) and not (
161-
USE_NINJA and not os.path.exists(ninja_build_file)
173+
cmake_cache_file_available = os.path.exists(self._cmake_cache_file)
174+
if cmake_cache_file_available:
175+
cmake_cache_variables = self.get_cmake_cache_variables()
176+
make_program: str | None = cmake_cache_variables.get("CMAKE_MAKE_PROGRAM") # type: ignore[assignment]
177+
if make_program and not shutil.which(make_program):
178+
# CMakeCache.txt exists, but the make program (e.g., ninja) does not.
179+
# See also: https://github.com/astral-sh/uv/issues/14269
180+
# This can happen if building with PEP-517 build isolation, where `ninja` was
181+
# installed in the isolated environment of the previous build run, but it has been
182+
# removed. The `ninja` executable with an old absolute path not available anymore.
183+
eprint(
184+
"!!!WARNING!!!: CMakeCache.txt exists, "
185+
f"but CMAKE_MAKE_PROGRAM ({make_program!r}) does not exist. "
186+
"Clearing CMake cache."
187+
)
188+
self.clear_cache()
189+
cmake_cache_file_available = False
190+
191+
if cmake_cache_file_available and (
192+
not USE_NINJA or os.path.exists(self._ninja_build_file)
162193
):
163194
# Everything's in place. Do not rerun.
164195
return
@@ -172,9 +203,9 @@ def generate(
172203
generator = os.getenv("CMAKE_GENERATOR", "Visual Studio 16 2019")
173204
supported = ["Visual Studio 16 2019", "Visual Studio 17 2022"]
174205
if generator not in supported:
175-
print("Unsupported `CMAKE_GENERATOR`: " + generator)
176-
print("Please set it to one of the following values: ")
177-
print("\n".join(supported))
206+
eprint("Unsupported `CMAKE_GENERATOR`: " + generator)
207+
eprint("Please set it to one of the following values: ")
208+
eprint("\n".join(supported))
178209
sys.exit(1)
179210
args.append("-G" + generator)
180211
toolset_dict = {}
@@ -183,7 +214,7 @@ def generate(
183214
toolset_dict["version"] = toolset_version
184215
curr_toolset = os.getenv("VCToolsVersion")
185216
if curr_toolset is None:
186-
print(
217+
eprint(
187218
"When you specify `CMAKE_GENERATOR_TOOLSET_VERSION`, you must also "
188219
"activate the vs environment of this version. Please read the notes "
189220
"in the build steps carefully."
@@ -328,7 +359,7 @@ def generate(
328359
# error if the user also attempts to set these CMAKE options directly.
329360
specified_cmake__options = set(build_options).intersection(cmake__options)
330361
if len(specified_cmake__options) > 0:
331-
print(
362+
eprint(
332363
", ".join(specified_cmake__options)
333364
+ " should not be specified in the environment variable. They are directly set by PyTorch build script."
334365
)
@@ -357,11 +388,8 @@ def generate(
357388
my_env[env_var_name] = str(my_env[env_var_name].encode("utf-8"))
358389
except UnicodeDecodeError as e:
359390
shex = ":".join(f"{ord(c):02x}" for c in my_env[env_var_name])
360-
print(
361-
f"Invalid ENV[{env_var_name}] = {shex}",
362-
file=sys.stderr,
363-
)
364-
print(e, file=sys.stderr)
391+
eprint(f"Invalid ENV[{env_var_name}] = {shex}")
392+
eprint(e)
365393
# According to the CMake manual, we should pass the arguments first,
366394
# and put the directory as the last element. Otherwise, these flags
367395
# may not be passed correctly.
@@ -372,7 +400,7 @@ def generate(
372400
self.run(args, env=my_env)
373401

374402
def build(self, my_env: dict[str, str]) -> None:
375-
"Runs cmake to build binaries."
403+
"""Runs cmake to build binaries."""
376404

377405
from .env import build_type
378406

@@ -410,3 +438,10 @@ def build(self, my_env: dict[str, str]) -> None:
410438
# CMake 3.12 provides a '-j' option.
411439
build_args += ["-j", max_jobs]
412440
self.run(build_args, my_env)
441+
442+
def clear_cache(self) -> None:
443+
"""Clears the CMake cache."""
444+
if os.path.isfile(self._cmake_cache_file):
445+
os.remove(self._cmake_cache_file)
446+
if os.path.isfile(self._ninja_build_file):
447+
os.remove(self._ninja_build_file)

0 commit comments

Comments
 (0)