-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathconftest.py
More file actions
166 lines (139 loc) · 5.5 KB
/
Copy pathconftest.py
File metadata and controls
166 lines (139 loc) · 5.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import pytest
import platform
import hashlib
import importlib.util
from pathlib import Path
def pytest_addoption(parser: pytest.Parser) -> None:
parser.addoption(
"--compile-guppy",
action="store_true",
default=False,
help="Regenerate checked-in LLVM IR files from inline guppy source in tests.",
)
@pytest.fixture
def compile_guppy(pytestconfig: pytest.Config) -> bool:
return bool(pytestconfig.getoption("--compile-guppy"))
def get_platform_suffix() -> str:
arch = platform.machine()
system = platform.system()
match arch.lower():
case "arm64" | "aarch64":
target_arch = "aarch64"
case "amd64" | "x86_64":
target_arch = "x86_64"
case _:
raise RuntimeError(f"Unsupported architecture: {arch}")
match system.lower():
case "linux":
target_system = "unknown-linux-gnu"
case "darwin" | "macos":
target_system = "apple-darwin"
case "windows":
target_system = "windows-gnu"
case _:
raise RuntimeError(f"Unsupported OS: {system}")
return f"{target_arch}-{target_system}"
SUPPORTED_TARGETS = [
"aarch64-apple-darwin",
"aarch64-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-unknown-linux-gnu",
"x86_64-windows-gnu",
]
SUPPORTED_QIS_PLATFORMS = [
"helios",
"sol",
]
def _compile_inline_guppy_source_to_hugr_bytes(guppy_source: str) -> bytes:
# check if guppy is installed
if importlib.util.find_spec("guppylang") is None:
raise RuntimeError(
"Guppy is not installed. Please install guppylang to compile inline guppy source."
)
# This executes trusted inline source defined in this repository's test files.
standalone_file = f"""
{guppy_source}
from pathlib import Path
compiled_hugr = main.compile()
current_dir = Path(__file__).parent
output_file = current_dir / "output.hugr"
output_file.write_bytes(compiled_hugr.to_bytes())
"""
# make temporary directory
import tempfile
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir) / "temp_guppy_source.py"
temp_path.write_text(standalone_file)
# execute the file in a subprocess to avoid any issues with stateful execution of guppy code in the same process
import subprocess
import sys
subprocess.run([sys.executable, f"{temp_path}"], check=True)
return (Path(temp_dir) / "output.hugr").read_bytes()
def _compile_hugr_to_llvm_ir_for_target(
hugr_bytes: bytes, qis_platform: str, target: str
) -> str:
try:
from selene_hugr_qis_compiler import compile_to_llvm_ir
except ImportError as exc:
raise RuntimeError(
"--compile-guppy requires selene_hugr_qis_compiler and guppylang to be installed."
) from exc
return compile_to_llvm_ir(hugr_bytes, platform=qis_platform, target_triple=target)
def _hash_guppy(guppy_source: str) -> str:
return hashlib.sha256(guppy_source.encode()).hexdigest()
def _compile_inline_guppy_source_to_llvm_ir(
guppy_source: str, *, qis_platform: str, target: str
) -> str:
hugr_bytes = _compile_inline_guppy_source_to_hugr_bytes(guppy_source)
return _compile_hugr_to_llvm_ir_for_target(
hugr_bytes, qis_platform=qis_platform, target=target
)
@pytest.fixture
def compiled_guppy(compile_guppy: bool, request: pytest.FixtureRequest):
def _resolve(
*,
program_name: str,
guppy_source: str,
qis_platform: str = "helios",
) -> Path | bytes:
test_name = request.node.name
test_path = Path(request.node.fspath)
resources_dir = (
test_path.parent / "resources" / "from_guppy" / test_path.stem / test_name
)
platform_file = (
resources_dir / f"{program_name}-{qis_platform}-{get_platform_suffix()}.ll"
)
sha_file = resources_dir / f"{program_name}.sha256"
input_sha256 = _hash_guppy(guppy_source)
if compile_guppy:
resources_dir.mkdir(parents=True, exist_ok=True)
sha_file.write_text(input_sha256)
for qis_platform_it in SUPPORTED_QIS_PLATFORMS:
for target in SUPPORTED_TARGETS:
llvm_ir = _compile_inline_guppy_source_to_llvm_ir(
guppy_source, qis_platform=qis_platform_it, target=target
)
(
resources_dir / f"{program_name}-{qis_platform_it}-{target}.ll"
).write_text(llvm_ir)
else:
if not sha_file.exists():
raise FileNotFoundError(
f"Missing SHA256 file for checked-in LLVM snapshot: {sha_file}. "
"Run pytest with --compile-guppy to generate it."
)
if not platform_file.exists():
raise FileNotFoundError(
f"Missing checked-in LLVM snapshot file: {platform_file}. "
"Run pytest with --compile-guppy to generate it."
)
stored_sha256 = sha_file.read_text()
if stored_sha256 != input_sha256:
raise ValueError(
f"SHA256 mismatch for {platform_file}. Expected {stored_sha256}, got {input_sha256}. "
"This likely means the inline guppy source was modified without regenerating the LLVM snapshot. "
"Run pytest with --compile-guppy to regenerate it."
)
return platform_file
return _resolve