Forked from
ESS EPICS Environment / wrappers / e3-require
285 commits behind the upstream repository.
-
Simon Rose authoredSimon Rose authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
utils.py 5.31 KiB
import os
import subprocess
import time
from pathlib import Path
from random import choice
from string import ascii_lowercase
from git import Repo
from run_iocsh import IOC
class Wrapper:
def __init__(
self, root_path: Path, *, name=None, include_dbd=True, url=None, **kwargs
):
test_env = os.environ.copy()
assert "EPICS_BASE" in test_env
assert "E3_REQUIRE_VERSION" in test_env
assert "EPICS_HOST_ARCH" in test_env
self.epics_base = Path(os.getenv("EPICS_BASE"))
self.base_version = self.epics_base.name.split("base-")[-1]
self.host_arch = os.getenv("EPICS_HOST_ARCH")
self.require_version = os.getenv("E3_REQUIRE_VERSION")
e3_require_config = (
self.epics_base / "require" / self.require_version / "configure"
)
assert e3_require_config.is_dir()
if name is None:
name = "test_mod_" + "".join(choice(ascii_lowercase) for _ in range(16))
self.name = name
self.version = "0.0.0+0"
self.path = root_path / f"e3-{self.name}"
self.config_dir = self.path / "configure"
self.config_module = self.config_dir / "CONFIG_MODULE"
module_path = (
self.name
if "E3_MODULE_SRC_PATH" not in kwargs
else kwargs["E3_MODULE_SRC_PATH"]
)
self.module_dir = self.path / module_path
self.build_dir = self.module_dir / f"O.{self.base_version}_{self.host_arch}"
self.makefile = self.path / "Makefile"
self.module_makefile = self.path / f"{self.name}.Makefile"
self.url = url
if self.url:
Repo.clone_from(self.url, self.path)
else:
self.module_dir.mkdir(parents=True)
self.config_dir.mkdir()
self.config_module.touch()
makefile_contents = f"""
TOP:=$(CURDIR)
E3_MODULE_NAME:={name}
E3_MODULE_VERSION:={self.version}
E3_MODULE_SRC_PATH:={module_path}
E3_MODULE_MAKEFILE:={name}.Makefile
include $(TOP)/configure/CONFIG_MODULE
-include $(TOP)/configure/CONFIG_MODULE.local
REQUIRE_CONFIG:={e3_require_config}
include $(REQUIRE_CONFIG)/CONFIG
include $(REQUIRE_CONFIG)/RULES_SITEMODS
"""
self.makefile.write_text(makefile_contents)
module_makefile_contents = """
where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include $(E3_REQUIRE_TOOLS)/driver.makefile
"""
self.module_makefile.write_text(module_makefile_contents)
if include_dbd:
self.add_file("test.dbd")
Repo.init(self.path)
def add_file(self, name):
(self.module_dir / name).touch()
def write_dot_local_data(self, config_file: str, config_vars: dict):
"""Write config data to the specific .local file."""
with open(self.config_dir / f"{config_file}.local", "w") as f:
for var, value in config_vars.items():
f.write(f"{var} = {value}\n")
def add_var_to_config_module(self, makefile_var: str, value: str, modifier="+"):
with open(self.config_module, "a") as f:
f.write(f"export {makefile_var} {modifier}= {value}\n")
def add_var_to_module_makefile(self, makefile_var: str, value: str, modifier="+"):
with open(self.module_makefile, "a") as f:
f.write(f"{makefile_var} {modifier}= {value}\n")
def get_env_var(self, var: str):
"""Fetch an environment variable from the module build environment."""
_, out, _ = self.run_make("cellvars")
for line in out.split("\n"):
if line.startswith(var):
return line.split("=")[1].strip()
return ""
def run_make(
self,
target: str,
*args,
module_version: str = "",
cell_path: str = "",
**kwargs,
):
"""Attempt to run `make <target> <args>` for the current wrapper."""
# We should not install in the global environment during tests
if target == "install":
target = "cellinstall"
if target == "uninstall":
target = "celluninstall"
env = os.environ.copy()
env.update(kwargs)
args = list(args)
if module_version:
args.append(f"E3_MODULE_VERSION={module_version}")
if not ("TEST_DEBUG_ARCH" in env and not env["TEST_DEBUG_ARCH"]):
args.append("EXCLUDE_ARCHS=debug")
if cell_path:
self.write_dot_local_data("CONFIG_CELL", {"E3_CELL_PATH": cell_path})
make_cmd = ["make", "-C", self.path, target] + args
p = subprocess.Popen(
make_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env=env,
)
outs, errs = p.communicate()
return p.returncode, outs, errs
def run_ioc_get_output(*args, **kwargs):
"""Run an IOC and try to load the test module."""
ioc_args = []
module = kwargs.get("module", None)
version = kwargs.get("version", None)
if module:
ioc_args.append("-r")
ioc_args.append(f"{module},{version}" if version else module)
cell_path = kwargs.get("cell_path", None)
if cell_path:
ioc_args.extend(["-l", cell_path])
ioc_args.extend(args)
with IOC(*ioc_args, ioc_executable="iocsh") as ioc:
time.sleep(1)
return ioc.proc.returncode, ioc.outs, ioc.errs