vunit-python-bridge 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. vunit_python_bridge/__init__.py +92 -0
  2. vunit_python_bridge/bin/vunit_python_bridge-cp310-win_amd64.dll +0 -0
  3. vunit_python_bridge/bin/vunit_python_bridge-cp311-win_amd64.dll +0 -0
  4. vunit_python_bridge/bin/vunit_python_bridge-cp312-win_amd64.dll +0 -0
  5. vunit_python_bridge/bin/vunit_python_bridge-cp313-win_amd64.dll +0 -0
  6. vunit_python_bridge/bin/vunit_python_bridge-cp314-win_amd64.dll +0 -0
  7. vunit_python_bridge/bridge.py +171 -0
  8. vunit_python_bridge/foreign_application.py +90 -0
  9. vunit_python_bridge/hdl/src/python_bridge_pkg.vhd.in +91 -0
  10. vunit_python_bridge/hdl/src/python_context.vhd +13 -0
  11. vunit_python_bridge/hdl/src/python_ffi_pkg_bridge.vhd +507 -0
  12. vunit_python_bridge/hdl/src/python_pkg.vhd +1087 -0
  13. vunit_python_bridge/hdl/src/python_pkg_vhpi.vhd +270 -0
  14. vunit_python_bridge/hdl/tools/generate_python_pkg.py +386 -0
  15. vunit_python_bridge/hdl/tools/python_pkg.vhd.in +765 -0
  16. vunit_python_bridge/native/arguments.c +191 -0
  17. vunit_python_bridge/native/bridge.h +117 -0
  18. vunit_python_bridge/native/config.c +201 -0
  19. vunit_python_bridge/native/error.c +120 -0
  20. vunit_python_bridge/native/fli.c +98 -0
  21. vunit_python_bridge/native/interpreter.c +271 -0
  22. vunit_python_bridge/native/operations.c +239 -0
  23. vunit_python_bridge/native/vhpi/python_pkg.c +125 -0
  24. vunit_python_bridge/native/vhpi/python_pkg.h +31 -0
  25. vunit_python_bridge/native/vhpi/python_pkg_vhpi.c +366 -0
  26. vunit_python_bridge/native_library.py +455 -0
  27. vunit_python_bridge/runtime.py +645 -0
  28. vunit_python_bridge/simulator_hooks.py +141 -0
  29. vunit_python_bridge/vunit_pkg.toml +17 -0
  30. vunit_python_bridge-0.1.0.dist-info/METADATA +116 -0
  31. vunit_python_bridge-0.1.0.dist-info/RECORD +34 -0
  32. vunit_python_bridge-0.1.0.dist-info/WHEEL +5 -0
  33. vunit_python_bridge-0.1.0.dist-info/licenses/LICENSE +373 -0
  34. vunit_python_bridge-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,455 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
4
+ #
5
+ # Copyright (c) 2014-2026, Lars Asplund lars.anders.asplund@gmail.com
6
+
7
+
8
+ """
9
+ The native bridge library: compiled and cached on Linux, prebuilt DLLs on Windows, compiled
10
+ with gcc on Windows for Questa/ModelSim and when a DLL is missing.
11
+
12
+ This module only depends on the standard library so that tools/build_python_bridge.py
13
+ can load it without VUnit's dependencies.
14
+ """
15
+
16
+ import hashlib
17
+ import os
18
+ import platform
19
+ import shlex
20
+ import shutil
21
+ import subprocess
22
+ import sys
23
+ import sysconfig
24
+ from pathlib import Path
25
+ from typing import Dict, List, Optional, Tuple
26
+
27
+ PACKAGE_PATH = Path(__file__).parent.resolve()
28
+ # C sources of the bridge library
29
+ NATIVE_PATH = PACKAGE_PATH / "native"
30
+ # Prebuilt Windows DLLs, included in releases
31
+ BINARY_PATH = PACKAGE_PATH / "bin"
32
+ # Front end only compiled into the FLI variant of the library
33
+ FLI_SOURCE_NAME = "fli.c"
34
+
35
+
36
+ class PythonBridgeError(RuntimeError):
37
+ """
38
+ The Python bridge cannot be set up in this environment, e.g. due to a missing prerequisite.
39
+ """
40
+
41
+
42
+ def check_python_build() -> None:
43
+ """
44
+ Reject Python builds that the bridge is known not to support.
45
+ """
46
+ if sysconfig.get_config_var("Py_GIL_DISABLED"):
47
+ raise PythonBridgeError(
48
+ "VHDL Python support does not work with free-threaded CPython builds "
49
+ f"({sys.executable}). Use a regular (GIL) CPython build."
50
+ )
51
+ if sys.implementation.name != "cpython":
52
+ raise PythonBridgeError(f"VHDL Python support requires CPython, not {sys.implementation.name}")
53
+
54
+
55
+ def prepare_library(root: Path, simulator_prefix: Optional[Path] = None) -> Path:
56
+ """
57
+ Path of the bridge library for the running Python, built or selected under root.
58
+
59
+ :param simulator_prefix: Executable directory of a simulator calling the bridge through
60
+ the FLI (Questa/ModelSim). The FLI variant of the library, which
61
+ also contains native/fli.c, is then built against its headers.
62
+ None selects the VHPIDIRECT variant used by NVC and GHDL.
63
+ """
64
+ if sys.platform == "win32":
65
+ add_python_dll_to_path()
66
+ if simulator_prefix is not None:
67
+ return _build_windows_library(root, simulator_prefix)
68
+ return _prepare_windows_library(root)
69
+ return _prepare_posix_library(root, simulator_prefix)
70
+
71
+
72
+ def windows_dll_name() -> str:
73
+ """
74
+ File name of the prebuilt bridge DLL for the running Python.
75
+ """
76
+ return f"vunit_python_bridge-cp{sys.version_info[0]}{sys.version_info[1]}-win_amd64.dll"
77
+
78
+
79
+ def windows_python_dll() -> str:
80
+ """
81
+ Path of the Python DLL of the running interpreter.
82
+ """
83
+ import ctypes # pylint: disable=import-outside-toplevel
84
+
85
+ dll_handle = getattr(sys, "dllhandle", None)
86
+ if dll_handle is None:
87
+ raise PythonBridgeError(
88
+ f"VHDL Python support requires a CPython build using a Python DLL ({sys.executable} has none)"
89
+ )
90
+ buffer = ctypes.create_unicode_buffer(32768)
91
+ kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]
92
+ length = kernel32.GetModuleFileNameW(ctypes.c_void_p(dll_handle), buffer, len(buffer))
93
+ if length == 0:
94
+ raise PythonBridgeError("Failed to determine the path of the Python DLL")
95
+ return buffer.value
96
+
97
+
98
+ def _check_windows_python() -> None:
99
+ """
100
+ Reject a Python that is not a 64-bit x86 (win-amd64) build.
101
+ """
102
+ if sysconfig.get_platform() != "win-amd64":
103
+ raise PythonBridgeError(
104
+ "VHDL Python support on Windows requires a 64-bit (x86-64) python.org style CPython, "
105
+ f"{sys.executable} is built for {sysconfig.get_platform()}"
106
+ )
107
+
108
+
109
+ # Machine field values of the PE (Windows executable) header
110
+ _PE_MACHINE_X64 = 0x8664
111
+ _PE_MACHINE_NAMES = {_PE_MACHINE_X64: "64-bit x86 (x64)", 0x014C: "32-bit x86", 0xAA64: "ARM64"}
112
+
113
+
114
+ def pe_machine(path: Path) -> Optional[int]:
115
+ """
116
+ The machine type in the PE header of a Windows executable, or None if it has no readable one.
117
+ """
118
+ try:
119
+ with open(path, "rb") as fptr:
120
+ header = fptr.read(4096)
121
+ except OSError:
122
+ return None
123
+ offset = int.from_bytes(header[0x3C:0x40], "little")
124
+ if header[:2] != b"MZ" or header[offset : offset + 4] != b"PE\0\0":
125
+ return None
126
+ return int.from_bytes(header[offset + 4 : offset + 6], "little")
127
+
128
+
129
+ def check_windows_64bit_simulator(simulator_name: str, simulator_prefix: Optional[str]) -> None:
130
+ """
131
+ On Windows, reject a Python or simulator that is not 64-bit x86. The bridge library and the
132
+ Python DLL it embeds are loaded into the simulator process, so all three must match, and
133
+ only 64-bit Python is supported. A mismatch otherwise fails late with an obscure load error.
134
+ A simulator executable that cannot be read is not checked.
135
+ """
136
+ if sys.platform != "win32":
137
+ return
138
+ _check_windows_python()
139
+ if simulator_prefix is None:
140
+ return
141
+ # NVC and GHDL name their executable, which VUnit lets the NVC and GHDL environment variables
142
+ # override, the Questa/ModelSim and Aldec interfaces use vsim
143
+ name = os.environ.get(simulator_name.upper(), simulator_name) if simulator_name in ("nvc", "ghdl") else "vsim"
144
+ executable = Path(simulator_prefix) / (name if name.lower().endswith(".exe") else f"{name}.exe")
145
+ machine = pe_machine(executable)
146
+ if machine is not None and machine != _PE_MACHINE_X64:
147
+ kind = _PE_MACHINE_NAMES.get(machine, f"machine type 0x{machine:04x}")
148
+ raise PythonBridgeError(
149
+ f"VHDL Python support requires a 64-bit (x64) simulator, but {executable!s} is {kind}. "
150
+ "Use a 64-bit installation of the simulator."
151
+ )
152
+
153
+
154
+ def _prepare_windows_library(root: Path) -> Path:
155
+ """
156
+ Select the prebuilt DLL for the running Python, or build the library with gcc when the
157
+ installation has none (e.g. a VUnit package built without the DLLs).
158
+ """
159
+ _check_windows_python()
160
+ if hasattr(sys, "gettotalrefcount"):
161
+ raise PythonBridgeError("VHDL Python support does not provide bridge DLLs for debug builds of CPython")
162
+
163
+ name = windows_dll_name()
164
+ source = BINARY_PATH / name
165
+ if not source.is_file():
166
+ return _build_windows_library(root)
167
+ data = source.read_bytes()
168
+ directory = root / f"cp{sys.version_info[0]}{sys.version_info[1]}-win_amd64-{hashlib.sha256(data).hexdigest()[:12]}"
169
+ target = directory / "vunit_python_bridge.dll"
170
+ if not (target.is_file() and target.read_bytes() == data):
171
+ directory.mkdir(parents=True, exist_ok=True)
172
+ tmp = target.with_name(target.name + f".{os.getpid()}.tmp")
173
+ tmp.write_bytes(data)
174
+ os.replace(tmp, target)
175
+ return target
176
+
177
+
178
+ def _python_library() -> Path:
179
+ """
180
+ The shared libpython of the running interpreter.
181
+
182
+ Regular --enable-shared builds name it in LDLIBRARY (libpython3.X.so, .dylib); macOS
183
+ framework builds name the framework binary instead (Python.framework/Versions/3.X/Python).
184
+ """
185
+ ldlibrary = sysconfig.get_config_var("LDLIBRARY") or ""
186
+ framework = sysconfig.get_config_var("PYTHONFRAMEWORK") or ""
187
+ version = f"{sys.version_info[0]}.{sys.version_info[1]}{getattr(sys, 'abiflags', '')}"
188
+ shared = bool(sysconfig.get_config_var("Py_ENABLE_SHARED")) or bool(framework)
189
+ if not shared or not (framework or ldlibrary.endswith((".so", ".dylib")) or ".so." in ldlibrary):
190
+ raise PythonBridgeError(
191
+ f"VHDL Python support requires a CPython built with a shared Python library (--enable-shared). "
192
+ f"{sys.executable} links Python statically "
193
+ f"(LDLIBRARY={ldlibrary!r}, Py_ENABLE_SHARED={sysconfig.get_config_var('Py_ENABLE_SHARED')!r})."
194
+ )
195
+ prefix = Path(sys.base_prefix)
196
+ candidates = [Path(sysconfig.get_config_var("LIBDIR") or "") / ldlibrary]
197
+ # Relocated installations (e.g. uv/python-build-standalone) may report a
198
+ # build-time LIBDIR, fall back to the installation prefix.
199
+ candidates.append(prefix / "lib" / ldlibrary)
200
+ if sys.platform == "darwin":
201
+ candidates.append(prefix / "lib" / f"libpython{version}.dylib")
202
+ if framework:
203
+ # The framework binary: the prefix of a framework build is Versions/3.X
204
+ candidates.append(prefix / framework)
205
+ framework_prefix = sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX") or ""
206
+ candidates.append(Path(framework_prefix) / f"{framework}.framework" / "Versions" / version / framework)
207
+ for candidate in candidates:
208
+ if candidate.is_file():
209
+ return candidate.resolve()
210
+ raise PythonBridgeError(
211
+ f"Failed to find the shared Python library {ldlibrary} of {sys.executable} "
212
+ f"(searched {', '.join(str(path) for path in candidates)})"
213
+ )
214
+
215
+
216
+ def _include_dirs(candidates: Optional[List[str]] = None) -> List[str]:
217
+ """
218
+ Include directories of the Python development headers, by default the ones sysconfig names.
219
+ """
220
+ result: List[str] = []
221
+ for path in candidates or [sysconfig.get_paths().get(name, "") for name in ("include", "platinclude")]:
222
+ if path and path not in result:
223
+ result.append(path)
224
+ for path in result:
225
+ if (Path(path) / "Python.h").is_file():
226
+ return result
227
+ raise PythonBridgeError(
228
+ f"VHDL Python support needs the Python development headers (Python.h) of {sys.executable} "
229
+ f"to build the Python bridge library, but they were not found in {', '.join(result)}. "
230
+ "Install the development package of your Python (e.g. python3-dev)."
231
+ )
232
+
233
+
234
+ def _compiler() -> List[str]:
235
+ """
236
+ The C compiler command: CC, the compiler Python was built with, or cc/gcc/clang.
237
+ """
238
+ if os.environ.get("CC"):
239
+ return shlex.split(os.environ["CC"])
240
+ configured = shlex.split(sysconfig.get_config_var("CC") or "")
241
+ if configured and shutil.which(configured[0]):
242
+ return configured[:1]
243
+ for name in ("cc", "gcc", "clang"):
244
+ if shutil.which(name):
245
+ return [name]
246
+ raise PythonBridgeError(
247
+ "VHDL Python support needs a C compiler (cc, gcc or clang) to build the Python bridge library. "
248
+ "Install one or set the CC environment variable."
249
+ )
250
+
251
+
252
+ def bridge_sources(fli: bool = False) -> List[Path]:
253
+ """
254
+ The C source files of the bridge library. fli adds the FLI front end.
255
+ """
256
+ sources = sorted(item for item in NATIVE_PATH.glob("*.c") if item.name != FLI_SOURCE_NAME)
257
+ if fli:
258
+ sources.append(NATIVE_PATH / FLI_SOURCE_NAME)
259
+ return sources
260
+
261
+
262
+ def _fli_include_dir(simulator_prefix: Path) -> str:
263
+ """
264
+ Directory of mti.h, next to the executable directory of the simulator.
265
+ """
266
+ include = Path(simulator_prefix).resolve().parent / "include"
267
+ if not (include / "mti.h").is_file():
268
+ raise PythonBridgeError(
269
+ f"VHDL Python support needs the FLI header mti.h of the simulator to build the Python "
270
+ f"bridge library, but it was not found in {include!s}"
271
+ )
272
+ return str(include)
273
+
274
+
275
+ def _source_fingerprint() -> str:
276
+ """
277
+ Hash of all bridge sources and headers.
278
+ """
279
+ digest = hashlib.sha256()
280
+ for path in sorted(NATIVE_PATH.glob("*.[ch]")):
281
+ digest.update(path.name.encode("utf-8") + b"\0" + path.read_bytes() + b"\0")
282
+ return digest.hexdigest()
283
+
284
+
285
+ def _posix_cache_directory(root: Path, python_library: Path, include_dirs: List[str]) -> Path:
286
+ """
287
+ Cache directory of a build: everything it depends on hashed into its name. The
288
+ include directories cover the FLI variant, whose simulator headers are among them.
289
+ """
290
+ key_items = [
291
+ _source_fingerprint(),
292
+ sys.platform,
293
+ platform.machine(),
294
+ " ".join(platform.libc_ver()),
295
+ sys.version,
296
+ str(sysconfig.get_config_var("SOABI")),
297
+ str(sysconfig.get_config_var("Py_GIL_DISABLED")),
298
+ str(python_library),
299
+ " ".join(include_dirs),
300
+ ]
301
+ key = hashlib.sha256("\n".join(key_items).encode("utf-8")).hexdigest()[:16]
302
+ tag = sysconfig.get_config_var("SOABI") or f"cp{sys.version_info[0]}{sys.version_info[1]}"
303
+ return root / f"{tag}-{key}"
304
+
305
+
306
+ def _prepare_posix_library(root: Path, simulator_prefix: Optional[Path] = None) -> Path:
307
+ """
308
+ Compile the bridge for the running Python, reusing a cached build when possible.
309
+
310
+ The FLI variant, selected with simulator_prefix, differs only in the added front end,
311
+ the added include directory and the file name, so both variants share this cache: the
312
+ simulator prefix is part of the key through its include directory.
313
+ """
314
+ fli = simulator_prefix is not None
315
+ python_library = _python_library()
316
+ include_dirs = _include_dirs()
317
+ if simulator_prefix is not None:
318
+ include_dirs = include_dirs + [_fli_include_dir(simulator_prefix)]
319
+
320
+ name = "libvunit_python_bridge_fli.so" if fli else "libvunit_python_bridge.so"
321
+ directory = _posix_cache_directory(root, python_library, include_dirs)
322
+ library_file = directory / name
323
+ if library_file.is_file():
324
+ return library_file
325
+
326
+ compiler = _compiler()
327
+ directory.mkdir(parents=True, exist_ok=True)
328
+ tmp = directory / f"{name}.{os.getpid()}.tmp"
329
+ cmd = (
330
+ compiler
331
+ + ["-shared", "-fPIC", "-O2", "-fvisibility=hidden"]
332
+ + [f"-I{path}" for path in include_dirs]
333
+ + [str(path) for path in bridge_sources(fli=fli)]
334
+ + ["-o", str(tmp)]
335
+ + [str(python_library), f"-Wl,-rpath,{python_library.parent!s}"]
336
+ + ([f"-Wl,-install_name,@rpath/{name}"] if sys.platform == "darwin" else [f"-Wl,-soname,{name}"])
337
+ + (["-ldl"] if sys.platform.startswith("linux") else [])
338
+ )
339
+ return compile_library(cmd, tmp, library_file)
340
+
341
+
342
+ def compile_library(cmd: List[str], tmp: Path, library_file: Path, env: Optional[Dict[str, str]] = None) -> Path:
343
+ """
344
+ Run a compiler command building tmp and move the result to library_file, failing with the
345
+ compiler output.
346
+ """
347
+ try:
348
+ proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=False, env=env)
349
+ except OSError as exc:
350
+ raise PythonBridgeError(f"Failed to run the C compiler {cmd[0]!r}: {exc}") from exc
351
+ if proc.returncode != 0:
352
+ tmp.unlink(missing_ok=True)
353
+ raise PythonBridgeError(
354
+ "Failed to build the Python bridge library:\n"
355
+ + " ".join(shlex.quote(item) for item in cmd)
356
+ + "\n"
357
+ + proc.stdout.decode(errors="replace")
358
+ )
359
+ if tmp != library_file:
360
+ os.replace(tmp, library_file)
361
+ return library_file
362
+
363
+
364
+ def windows_gcc(simulator_prefix: Optional[Path] = None) -> Optional[List[str]]:
365
+ """
366
+ The C compiler command for builds on Windows, or None: CC, else the MinGW gcc bundled with
367
+ the simulator (Questa/ModelSim: gcc-*-mingw64*, Riviera-PRO: mingw), else gcc on PATH.
368
+ Not every installation bundles a gcc, e.g. Questa on Windows often does not.
369
+ """
370
+ if os.environ.get("CC"):
371
+ return shlex.split(os.environ["CC"])
372
+ if simulator_prefix is not None:
373
+ install = Path(simulator_prefix).resolve().parent
374
+ bundled = sorted(install.glob("gcc*mingw64*/bin/gcc.exe")) + sorted(install.glob("mingw*/bin/gcc.exe"))
375
+ if bundled:
376
+ return [str(bundled[0])]
377
+ gcc = shutil.which("gcc")
378
+ return [gcc] if gcc else None
379
+
380
+
381
+ def python_dev_paths() -> Tuple[str, Path]:
382
+ """
383
+ The directory of Python.h and of the Python library to link against, for the Python running
384
+ VUnit. Both belong to the base installation, also when VUnit runs in a virtual environment,
385
+ which has neither.
386
+ """
387
+ include = next(
388
+ path
389
+ for path in _include_dirs([sysconfig.get_paths()["include"], str(Path(sys.base_prefix) / "include")])
390
+ if (Path(path) / "Python.h").is_file()
391
+ )
392
+ if sys.platform == "win32":
393
+ return include, Path(sys.base_prefix) / "libs"
394
+ return include, Path(sysconfig.get_config_var("LIBDIR") or Path(sys.base_prefix) / "lib")
395
+
396
+
397
+ def add_python_dll_to_path() -> None:
398
+ """
399
+ Libraries built with gcc on Windows import the Python DLL directly (the prebuilt DLLs load it
400
+ themselves), so the simulator processes VUnit starts must find it through PATH.
401
+ """
402
+ # ponytail: changes PATH of the whole VUnit process, only for its children to find pythonXY.dll
403
+ directory = str(Path(windows_python_dll()).parent)
404
+ paths = os.environ.get("PATH", "").split(os.pathsep)
405
+ if directory not in paths:
406
+ os.environ["PATH"] = os.pathsep.join([item for item in paths if item] + [directory])
407
+
408
+
409
+ def _build_windows_library(root: Path, simulator_prefix: Optional[Path] = None) -> Path:
410
+ """
411
+ Build the library on Windows with gcc (see windows_gcc) against the headers and the import
412
+ library of the Python running VUnit, reusing a cached build like the POSIX one.
413
+
414
+ With simulator_prefix, the FLI variant for Questa/ModelSim: the prebuilt DLLs cannot be used
415
+ for it, since they are MSVC builds without the FLI front end, which has to be linked against
416
+ the simulator's own libmtipli. Without, the VHPIDIRECT variant for NVC and GHDL, built only
417
+ when there is no prebuilt DLL.
418
+
419
+ Untested: this environment has no Windows installation.
420
+ """
421
+ fli = simulator_prefix is not None
422
+ compiler = windows_gcc(simulator_prefix)
423
+ if compiler is None:
424
+ bundled = ""
425
+ if simulator_prefix is not None:
426
+ bundled = f"the simulator installation ({Path(simulator_prefix).resolve().parent!s}) bundles none and "
427
+ raise PythonBridgeError(
428
+ "VHDL Python support on Windows needs gcc (MinGW-w64) to build the Python bridge library, but "
429
+ f"{bundled}there is no gcc on PATH. Install MinGW-w64 and put its bin directory on PATH, "
430
+ "or set the CC environment variable."
431
+ )
432
+ python_include, python_libs = python_dev_paths()
433
+ include_dirs = [python_include] + ([_fli_include_dir(simulator_prefix)] if simulator_prefix is not None else [])
434
+
435
+ name = "vunit_python_bridge_fli.dll" if fli else "vunit_python_bridge.dll"
436
+ key_items = [_source_fingerprint(), sys.version, sys.base_prefix, str(simulator_prefix), " ".join(compiler)]
437
+ key = hashlib.sha256("\n".join(key_items).encode("utf-8")).hexdigest()[:16]
438
+ directory = root / f"cp{sys.version_info[0]}{sys.version_info[1]}-win_amd64-{'fli' if fli else 'gcc'}-{key}"
439
+ library_file = directory / name
440
+ if library_file.is_file():
441
+ return library_file
442
+
443
+ directory.mkdir(parents=True, exist_ok=True)
444
+ tmp = directory / f"{name}.{os.getpid()}.tmp"
445
+ cmd = (
446
+ compiler
447
+ + ["-shared", "-m64", "-O2", "-D__USE_MINGW_ANSI_STDIO=1"]
448
+ + (["-freg-struct-return"] if fli else [])
449
+ + [f"-I{path}" for path in include_dirs]
450
+ + [str(path) for path in bridge_sources(fli=fli)]
451
+ + ["-o", str(tmp)]
452
+ + [f"-L{python_libs!s}", f"-lpython{sys.version_info[0]}{sys.version_info[1]}"]
453
+ + ([f"-L{Path(simulator_prefix).resolve()!s}", "-lmtipli"] if simulator_prefix is not None else [])
454
+ )
455
+ return compile_library(cmd, tmp, library_file)