Source code for pyam.cunit
# Copyright 2023, Dr John A.R. Williams
# SPDX-License-Identifier: GPL-3.0-only
"""C compilation and program execution functions
Typical Usage:
.. code-block:
binary_path=c_compile(binary_path,"mysouce.c",include=["include_path"],declarations=['DOSOMETHING'])
ran_ok=c_exec(binary_path, options=( "option1", "option2" ), timeout=5)
"""
from pathlib import Path
from subprocess import run
from typing import Union, Sequence, List
[docs]class CompilationError(Exception):
"""Error in C compilation"""
[docs]class RunTimeError(Exception):
"""Error in binary executation"""
[docs]class LintError(Exception):
"""Exceeded maximum number of lint warnings"""
[docs]def c_compile(
binary: Union[Path, str],
source: Union[Path, str],
include: Sequence[Union[Path, str]] = (),
cflags: Sequence[str] = (),
declarations: Sequence[str] = (),
compiler: str = "gcc",
link: Sequence[str] = ()) -> Union[Path, str]:
"""Use C compile to compile source files into an executable binary
Args:
binary: location for binary executable
sources: source files to compile
include: include paths to search for headers
cflags: additional flags to add during compilation
declarations: list of compile declarations (-D flags)
compiler: name of compiler to use
link: sequence of flags to pass for linkage
Raises:
CompilationError: if compiler failed
Returns:
Location of binary executable
"""
# pylint: disable=W1510
include = [(lambda s: f"-I{s}")(s) for s in include]
for dec in declarations:
cflags = cflags + ["-D", dec]
result = run(
(compiler, "-o", str(binary), *include, *cflags, str(source), *link),
text=True,
capture_output=True)
if result.returncode == 0:
return binary
raise CompilationError(result.stderr + result.stdout)
[docs]def c_exec(binary: Union[Path, str],
flags: Sequence[str] = (),
input: Union[List[str], str] = "",
timeout: Union[float, str] = None) -> True:
"""Execute a binary executable with given flags.
Args:
binary: Path to binary executable
timeout: timeout for process
input : A string that will be fed to standard input or
a list of strings can be given - these will be joined with a newline character.
Returns:
subprocess.CompletedProcess: From the run
"""
if not isinstance(input, str):
input = "\n".join([str(a) for a in input]) + "\n"
# pylint: disable=W1510
return run((str(binary), *flags),
text=True,
input=input,
capture_output=True,
timeout=timeout)