Skip to content

Commit bfca4d2

Browse files
authored
fix: adjust the timeout when running on rosetta (#1131)
Testing a possible solution to #1127. Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
1 parent 8d070dc commit bfca4d2

File tree

1 file changed

+58
-9
lines changed

1 file changed

+58
-9
lines changed

src/scikit_build_core/program_search.py

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from __future__ import annotations
22

33
import contextlib
4+
import functools
45
import json
56
import os
7+
import platform
68
import shutil
79
import subprocess
810
import sys
@@ -33,12 +35,53 @@ def __dir__() -> list[str]:
3335
return __all__
3436

3537

36-
# Make sure we don't wait forever for programs to respond
37-
# CI services can be really slow under load
38-
if os.environ.get("CI", ""):
39-
TIMEOUT = 20
40-
else:
41-
TIMEOUT = 10 if sys.platform.startswith("win") else 5
38+
BASE_TIMEOUT = 5
39+
40+
41+
def _macos_binary_is_x86(path: Path) -> bool:
42+
"""
43+
Returns True if the binary is x86. Only run on macOS.
44+
"""
45+
try:
46+
# lipo gives clean output like: "Architectures in the fat file: ... are: x86_64 arm64"
47+
out = subprocess.check_output(["lipo", "-info", path], text=True)
48+
except (FileNotFoundError, subprocess.CalledProcessError):
49+
# Fallback to 'file' if lipo not available or fails
50+
try:
51+
out = subprocess.check_output(["file", path], text=True)
52+
except (FileNotFoundError, subprocess.CalledProcessError):
53+
return False # unknown, assume not x86
54+
55+
# Ignore native or fat binaries
56+
if "arm64" in out:
57+
return False
58+
59+
return "x86_64" in out or "i386" in out
60+
61+
62+
@functools.lru_cache(None)
63+
def compute_timeout(executable: Path) -> int:
64+
"""
65+
Compute a recommended timeout. Takes the base timeout and
66+
multiplies it based on various factors:
67+
68+
* Is on CI: quadruples it
69+
* Is windows: doubles it
70+
* Runs with Rosetta: triples it
71+
72+
These do not stack.
73+
"""
74+
75+
if os.environ.get("CI", ""):
76+
return BASE_TIMEOUT * 4
77+
78+
if sys.platform.startswith("win"):
79+
return BASE_TIMEOUT * 2
80+
81+
if sys.platform == "darwin" and platform.machine() == "arm64":
82+
return BASE_TIMEOUT * 3 if _macos_binary_is_x86(executable) else BASE_TIMEOUT
83+
84+
return BASE_TIMEOUT
4285

4386

4487
class Program(NamedTuple):
@@ -89,7 +132,9 @@ def get_cmake_program(cmake_path: Path) -> Program:
89132
None if it cannot be determined.
90133
"""
91134
try:
92-
result = Run(timeout=TIMEOUT).capture(cmake_path, "-E", "capabilities")
135+
result = Run(timeout=compute_timeout(cmake_path)).capture(
136+
cmake_path, "-E", "capabilities"
137+
)
93138
try:
94139
version = Version(
95140
json.loads(result.stdout)["version"]["string"].split("-")[0]
@@ -100,7 +145,9 @@ def get_cmake_program(cmake_path: Path) -> Program:
100145
logger.warning("Could not determine CMake version, got {!r}", result.stdout)
101146
except subprocess.CalledProcessError:
102147
try:
103-
result = Run(timeout=TIMEOUT).capture(cmake_path, "--version")
148+
result = Run(timeout=compute_timeout(cmake_path)).capture(
149+
cmake_path, "--version"
150+
)
104151
try:
105152
version = Version(
106153
result.stdout.splitlines()[0].split()[-1].split("-")[0]
@@ -144,7 +191,9 @@ def get_ninja_programs(*, module: bool = True) -> Generator[Program, None, None]
144191
"""
145192
for ninja_path in _get_ninja_path(module=module):
146193
try:
147-
result = Run(timeout=TIMEOUT).capture(ninja_path, "--version")
194+
result = Run(timeout=compute_timeout(ninja_path)).capture(
195+
ninja_path, "--version"
196+
)
148197
except (
149198
subprocess.CalledProcessError,
150199
PermissionError,

0 commit comments

Comments
 (0)