Build executable from Python
We often use executables from Python with data transfer via:
- stdin/stdout (small transfers, less than a megabyte)
- temporary files (arbitrarily large data)
This provides a language-agnostic interface that we can use from other scripted languages like Matlab or Julia, future-proofing efforts at the price of some runtime efficiency due to the out-of-core data transfer.
Here is a snipping we use to compile a single C code executable from Python (from GeoRINEX program):
"""
save this in say src/mypkg/build.py
then from the code that needs the output executable, say "myprog.bin":
from .build import build
exe = "myprog.bin"
...
if not exe.is_file():
build("src/myprog.c")
# code that passes data via stdin/stdout and/or files using subprocess.run()
"""
import subprocess
import shutil
from pathlib import Path
def build(src: Path, cc: str = None) -> int:
"""
Parameters
----------
src: pathlib.Path
path to single C source file.
cc: str, optional
desired compiler path or name
Returns
-------
ret: int
return code from compiler (0 is success)
"""
if cc:
return do_compile(cc, src)
compilers = ["cc", "gcc", "clang", "icx", "clang-cl"]
ret = 1
for cc in compilers:
if shutil.which(cc):
ret = do_compile(cc, src)
if ret == 0:
break
return ret
def do_compile(cc: str, src: Path) -> int:
if not src.is_file():
raise FileNotFoundError(src)
if cc.endswith("cl"): # msvc-like
cmd = [cc, str(src), f"/Fe:{src.parent}"]
else:
cmd = [cc, str(src), "-O2", f"-o{src.with_suffix('.bin')}"]
ret = subprocess.run(cmd).returncode
return ret