Skip to content

Commit 80e661f

Browse files
authored
Merge pull request #1398 from haddocking/1390-when-using-modegrid-make-sure-the-cns-binary-is-the-linux-one
force download of cns linux binary if `arch!="x86_64-linux"`
2 parents e302b58 + fa51373 commit 80e661f

File tree

6 files changed

+137
-24
lines changed

6 files changed

+137
-24
lines changed

integration_tests/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import os
2+
import platform
23
from pathlib import Path
34

4-
from haddock.libs.libgrid import ping_dirac
55
import pytest
66

7+
from haddock.libs.libgrid import ping_dirac
8+
79
if "CNS_EXEC" in os.environ:
810
CNS_EXEC = os.environ["CNS_EXEC"]
911
else:
@@ -29,3 +31,11 @@
2931

3032
has_mpi = pytest.mark.skipif(not MPI_ENABLED, reason="MPI is not enabled")
3133
has_grid = pytest.mark.skipif(not ping_dirac(), reason="Dirac not reachable")
34+
is_linux_x86_64 = pytest.mark.skipif(
35+
platform.system().lower() != "linux" or platform.machine().lower() != "x86_64",
36+
reason="Only runs on x86_64 Linux systems",
37+
)
38+
is_not_linux_x86_64 = pytest.mark.skipif(
39+
platform.system().lower() == "linux" and platform.machine().lower() == "x86_64",
40+
reason="Only runs on non-x86_64-linux systems",
41+
)

integration_tests/test_libutil.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
5+
from haddock.libs.libutil import get_cns_executable
6+
7+
from . import is_linux_x86_64, is_not_linux_x86_64
8+
9+
10+
def test_finds_default_cns_executable():
11+
"""Test that the function finds CNS in the default location."""
12+
cns_exec, cns_exec_linux = get_cns_executable()
13+
14+
assert cns_exec.exists(), f"CNS executable not found at {cns_exec}"
15+
assert isinstance(cns_exec, Path)
16+
assert isinstance(cns_exec_linux, Path)
17+
18+
19+
def test_cns_executable_is_file():
20+
"""Test that the CNS executable is actually a file."""
21+
cns_exec, _ = get_cns_executable()
22+
23+
assert cns_exec.is_file(), f"{cns_exec} is not a file"
24+
25+
26+
@is_linux_x86_64
27+
def test_linux_x86_64_returns_same_executable():
28+
"""On x86_64 Linux, both executables should be the same."""
29+
cns_exec, cns_exec_linux = get_cns_executable()
30+
31+
assert cns_exec == cns_exec_linux
32+
33+
34+
@is_not_linux_x86_64
35+
def test_non_linux_looks_for_linux_variant():
36+
"""On non-Linux systems, should look for _linux variant."""
37+
cns_exec, cns_exec_linux = get_cns_executable()
38+
39+
assert cns_exec != cns_exec_linux
40+
assert cns_exec_linux.name == f"{cns_exec.name}_linux"
41+
42+
43+
def test_returns_tuple_of_paths():
44+
"""Test that function returns a tuple of two Path objects."""
45+
result = get_cns_executable()
46+
47+
assert isinstance(result, tuple)
48+
assert len(result) == 2
49+
assert all(isinstance(p, Path) for p in result)

setup.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@ def download_binaries(self):
131131
print(msg)
132132
sys.exit(1)
133133

134+
if arch != "x86_64-linux" and filename.name == "cns":
135+
# Force the download of the linux binary, this is needed for GRID executions
136+
download_path = Path(target_bin_dir, "cns_linux")
137+
status, msg = self.download_file(
138+
binary_dict["x86_64-linux"], download_path
139+
)
140+
if not status:
141+
print(msg)
142+
sys.exit(1)
143+
134144
if "".join(filename.suffixes) == ".tar.gz":
135145
try:
136146
with tarfile.open(download_path, "r:gz") as tar:

src/haddock/core/defaults.py

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,15 @@
11
"""All default parameters used by the framework."""
22

3-
import os
43
import string
5-
import sys
64
from importlib.resources import files
75
from pathlib import Path
86

97
import yaml
108

11-
import haddock
12-
from haddock import core_path, log
13-
14-
15-
cns_exec = Path(files(haddock).joinpath("bin/cns")) # type: ignore
16-
if not cns_exec.exists():
17-
log.warning("CNS executable not found at %s", cns_exec)
18-
_cns_exec = os.environ.get("CNS_EXEC")
19-
if _cns_exec is None:
20-
log.error(
21-
"Please define the location the CNS binary by setting a CNS_EXEC system variable"
22-
)
23-
sys.exit(1)
24-
else:
25-
cns_exec = Path(_cns_exec)
9+
from haddock import core_path
10+
from haddock.libs.libutil import get_cns_executable
11+
12+
cns_exec, cns_exec_linux = get_cns_executable()
2613

2714
CONTACT_FCC_EXEC = Path(files("haddock").joinpath("bin/contact_fcc")) # type: ignore
2815
FAST_RMSDMATRIX_EXEC = Path(files("haddock").joinpath("bin/fast-rmsdmatrix")) # type: ignore

src/haddock/libs/libgrid.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from typing import Optional, Tuple, Union
1818

1919
from haddock import log
20-
from haddock.core.defaults import cns_exec as CNS_EXEC
20+
from haddock.core.defaults import cns_exec_linux as CNS_EXEC
2121
from haddock.libs.libsubprocess import CNSJob
2222
from haddock.libs.libutil import parse_ncores
2323

src/haddock/libs/libutil.py

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
"""General utilities."""
2+
23
import collections.abc
34
import contextlib
45
import os
6+
import platform
57
import re
68
import shutil
79
import subprocess
810
import sys
911
from copy import deepcopy
1012
from functools import partial
13+
from importlib.resources import files
1114
from pathlib import Path
1215

16+
import haddock
1317
from haddock import EmptyPath, log
1418
from haddock.core.exceptions import SetupError
1519
from haddock.core.typing import (
@@ -30,7 +34,6 @@
3034
)
3135
from haddock.gear.greetings import get_goodbye_help
3236

33-
3437
check_subprocess = partial(
3538
subprocess.run,
3639
shell=True,
@@ -62,7 +65,7 @@ def make_list_if_string(item: Union[str, list[str]]) -> list[str]:
6265

6366

6467
def transform_to_list(
65-
item: Union[Iterable[AnyT], AnyT]
68+
item: Union[Iterable[AnyT], AnyT],
6669
) -> Union[list[AnyT], tuple[AnyT, ...]]:
6770
"""
6871
Put `item` into a list if not a list already.
@@ -130,7 +133,7 @@ def remove_dict_keys(d: ParamMap, keys: Container[str]) -> ParamDict:
130133

131134
def cpu_count() -> int:
132135
"""Count number of available CPU for the process.
133-
136+
134137
User suggestion, by https://github.com/EricDeveaud
135138
136139
Note: pid 0 == current process
@@ -142,9 +145,10 @@ def cpu_count() -> int:
142145
process_ncores : int
143146
Number of cores allocated to the process pid.
144147
"""
148+
145149
def _cpu_count() -> int:
146150
"""Detect number of cores available.
147-
151+
148152
Returns
149153
-------
150154
ncores : int
@@ -159,7 +163,7 @@ def _cpu_count() -> int:
159163
ncores = int(_process_ncores)
160164
except AttributeError:
161165
# If unsucessful, return the number cores detected in the machine
162-
# Note: this can happen on MacOS, where the os.sched_getaffinity
166+
# Note: this can happen on MacOS, where the os.sched_getaffinity
163167
# may not be defined/exist.
164168
ncores = int(os.cpu_count())
165169
return ncores
@@ -422,3 +426,56 @@ def recursive_convert_paths_to_strings(params: ParamMapT) -> ParamMapT:
422426
params[param] = value
423427

424428
return params
429+
430+
431+
def get_cns_executable() -> tuple[Path, Path]:
432+
"""
433+
Locate CNS executable and its Linux variant for grid execution.
434+
435+
Returns:
436+
Tuple of (cns_exec, cns_exec_linux) paths
437+
438+
Raises:
439+
SystemExit: If CNS executable cannot be found
440+
"""
441+
# Try default location first
442+
cns_exec = Path(files(haddock).joinpath("bin/cns")) # type: ignore
443+
444+
if not cns_exec.exists():
445+
log.error("CNS executable not found at %s", cns_exec)
446+
447+
# Fall back to environment variable
448+
cns_exec_env = os.environ.get("CNS_EXEC")
449+
if cns_exec_env is None:
450+
log.error(
451+
"Please define the CNS binary location by setting the CNS_EXEC "
452+
"environment variable"
453+
)
454+
sys.exit(1)
455+
456+
cns_exec = Path(cns_exec_env)
457+
if not cns_exec.exists():
458+
log.error("CNS executable not found at %s", cns_exec)
459+
sys.exit(1)
460+
461+
# Determine Linux-specific executable for grid usage
462+
system = platform.system().lower()
463+
machine = platform.machine().lower()
464+
current_arch = f"{machine}-{system}"
465+
466+
if current_arch == "x86_64-linux":
467+
cns_exec_linux = cns_exec
468+
else:
469+
# Look for Linux binary variant
470+
cns_exec_linux = cns_exec.with_name(f"{cns_exec.name}_linux")
471+
472+
if not cns_exec_linux.exists():
473+
log.warning(
474+
"Current architecture is %s, but grid execution requires "
475+
"an x86_64-linux binary",
476+
current_arch,
477+
)
478+
log.warning("Linux CNS binary not found at %s", cns_exec_linux)
479+
log.warning("GRID mode will not be available")
480+
481+
return cns_exec, cns_exec_linux

0 commit comments

Comments
 (0)