diff --git a/CHANGELOG.md b/CHANGELOG.md index 091964fb76adc46492045af248628073dc0ede8a..2e1660cd79aaf446d572ec72db60babf8ec9e611 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 not by require. * The loop over `EPICSVERSION` in `driver.makefile` has been removed; various other cleanup has been performed. * Improved output during IOC startup +* e3-sequencer tests added, as well as removing pre-processing of .stt files. ## [4.0.0] diff --git a/require-ess/tools/driver.makefile b/require-ess/tools/driver.makefile index 66d3d0877224ed1d402f98b22d25d081928e51d8..c4ddc74ccbeb78e1d49ffb4358a3da3a0d5d2e84 100644 --- a/require-ess/tools/driver.makefile +++ b/require-ess/tools/driver.makefile @@ -514,7 +514,7 @@ DBDFILES += $(patsubst %.stt,%_snl.dbd,$(notdir $(filter %.stt,${SRCS}))) DBDFILES += $(patsubst %.gt,%.dbd,$(notdir $(filter %.gt,${SRCS}))) # snc location -SNCALL=$(shell ls -dv $(E3_SITEMODS_PATH)/sequencer/$(sequencer_VERSION)/bin/$(EPICS_HOST_ARCH) 2> /dev/null)) +SNCALL=$(shell ls -dv $(E3_SITEMODS_PATH)/sequencer/$(sequencer_VERSION)/bin/$(EPICS_HOST_ARCH) 2> /dev/null) SNC=$(lastword $(SNCALL))/snc ifneq (,$(strip $(VLIBS))) @@ -698,59 +698,35 @@ ${INSTALL_BINS}: $(addprefix ../,$(filter-out /%,${BINS})) $(filter /%,${BINS}) # Create SNL code from st/stt file. # Important to have %.o: %.st and %.o: %.stt rule before %.o: %.c rule! -# Preprocess in any case because docu and implemented EPICS rules mismatch here. CPPSNCFLAGS1 = $(filter -D%, ${OP_SYS_CFLAGS}) CPPSNCFLAGS1 += $(filter-out ${OP_SYS_INCLUDE_CPPFLAGS} ,${CPPFLAGS}) ${CPPSNCFLAGS} CPPSNCFLAGS1 += -I $(dir $(SNC))../../include SNCFLAGS += -r +%.i: %.st + @echo ">> Preprocessing $(<F)" + $(CPP) ${CPPSNCFLAGS1} $< > $(*F).i -# 1) ESS uses 7.0.3.1 as the minimal EPICS BASE, so we don't need to check 3.13, -# 2) We also need -c option in $(COMPILE.c) in order to compile generated source file properly -# 3) SNC (2.1.21) should use -o, because without them, snc returns $(*F).i.c instead of $(*F).c -# With the EPICS standard building rule, -o and mv are used. -# -# Tuesday, November 28 15:59:37 CET 2017, Jeong Han Lee - - -%$(OBJ) %_snl.dbd: %.st +%.c: %.i @echo "" @echo ">> SNC building process .... " @echo ">> SNC : $(SNC)" @echo ">> SNC_VERSION : $(sequencer_VERSION)" - @echo ">> Preprocessing $(<F)" - $(RM) $(*F).i - $(CPP) ${CPPSNCFLAGS1} $< > $(*F).i - @echo ">> Converting $(*F).i to $(*F).c" - $(RM) $@ @echo ">> SNC is defined as $(SNC)" - $(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $(*F).i -o $(*F).c.tmp - @mv $(*F).c.tmp $(*F).c - @echo ">> Compiling $(*F).c" - $(RM) $@ - $(COMPILE.c) -c ${SNC_CFLAGS} $(*F).c + $(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $(*F).i -o $(*F).c + +%_snl.dbd: %.c @echo ">> Building $(*F)_snl.dbd" awk -F [\(\)] '/epicsExportRegistrar/ { print "registrar (" $$2 ")"}' $(*F).c > $(*F)_snl.dbd -%$(OBJ) %_snl.dbd: %.stt +%.c: %.stt @echo "" @echo ">> SNC building process .... " @echo ">> SNC : $(SNC)" @echo ">> SNC_VERSION : $(sequencer_VERSION)" - @echo ">> Preprocessing $(<F)" - $(RM) $(*F).i - $(CPP) ${CPPSNCFLAGS1} $< > $(*F).i - @echo ">> Converting $(*F).i to $(*F).c" - $(RM) $@ @echo ">> SNC is defined as $(SNC)" - $(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $(*F).i -o $(*F).c.tmp - @mv $(*F).c.tmp $(*F).c - @echo ">> Compiling $(*F).c" - $(RM) $@ - $(COMPILE.c) -c ${SNC_CFLAGS} $(*F).c - @echo "Building $(*F)_snl.dbd" - awk -F [\(\)] '/epicsExportRegistrar/ { print "registrar(" $$2 ")"}' $(*F).c > $(*F)_snl.dbd + $(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $< -o $(*F).c # Create GPIB code from *.gt file. diff --git a/tests/test_sequencer.py b/tests/test_sequencer.py new file mode 100644 index 0000000000000000000000000000000000000000..aa743cb7209a07957d1baa3804c0a1be883c53ce --- /dev/null +++ b/tests/test_sequencer.py @@ -0,0 +1,130 @@ +import pathlib + +import pytest + +from .utils import Wrapper + +GITLAB_URL = "https://gitlab.esss.lu.se" + +TEST_SEQ_SRC = """ +program test + +ss ss1 {{ + state init {{ + when(delay(1)) {{ + printf({}); + }} + state init + }} +}} +""" + + +class Sequencer(Wrapper): + sequencer_url = f"{GITLAB_URL}/e3/wrappers/core/e3-sequencer.git" + + def __init__(self, path: pathlib.Path): + super().__init__(path, name="sequencer", url=self.sequencer_url) + + self.version = "sequencer_test" + + self.write_dot_local_data( + "RELEASE", + {"EPICS_BASE": self.epics_base, "E3_REQUIRE_VERSION": self.require_version}, + ) + self.write_dot_local_data("CONFIG_MODULE", {"E3_MODULE_VERSION": self.version}) + + self.cell_path = self.path / "cellMods" + + rc, *_ = self.run_make("init") + assert rc == 0 + rc, *_ = self.run_make("patch") + assert rc == 0 + rc, *_ = self.run_make("build") + assert rc == 0 + rc, *_ = self.run_make("cellinstall", cell_path=self.cell_path) + assert rc == 0 + + self.snc_path = ( + self.cell_path + / f"base-{self.base_version}" + / f"require-{self.require_version}" + / "sequencer" + / self.version + / "bin" + / self.host_arch + / "snc" + ) + assert self.snc_path.is_file() + + +@pytest.fixture(scope="module") +def sequencer(tmp_path_factory): + path = tmp_path_factory.mktemp("sequencer_build") + yield Sequencer(path) + + +class SNLWrapper(Wrapper): + def __init__(self, sequencer: Sequencer, path: pathlib.Path): + super().__init__(path) + self.sequencer = sequencer + + self.add_var_to_config_module("SEQUENCER_DEP_VERSION", self.sequencer.version) + + def run_make(self, *args): + return super().run_make( + *args, + f"E3_CELL_PATH={self.sequencer.cell_path}", + f"SNC={self.sequencer.snc_path}", + ) + + +@pytest.fixture +def snl_wrapper(sequencer, tmp_path): + yield SNLWrapper(sequencer, tmp_path) + + +@pytest.mark.parametrize("extension", ["st", "stt"]) +def test_compile_snl_file(snl_wrapper: SNLWrapper, extension): + snl_filename = "test_filename" + seq_source = snl_wrapper.module_dir / f"{snl_filename}.{extension}" + seq_source.write_text(TEST_SEQ_SRC.format('""')) + + snl_wrapper.add_var_to_module_makefile("SOURCES", f"{snl_filename}.{extension}") + + rc, *_ = snl_wrapper.run_make("cellbuild") + assert rc == 0 + + assert (snl_wrapper.build_dir / f"{snl_filename}.o").is_file() + assert (snl_wrapper.build_dir / f"{snl_filename}_snl.dbd").is_file() + + +def test_preprocess_st_file(snl_wrapper: SNLWrapper): + snl_filename = "test_file.st" + seq_src = snl_wrapper.module_dir / snl_filename + seq_src.write_text( + '#define MESSAGE "waiting\\n"\n' + TEST_SEQ_SRC.format("MESSAGE") + ) + + snl_wrapper.add_var_to_module_makefile("SOURCES", snl_filename) + + rc, *_ = snl_wrapper.run_make("cellbuild") + assert rc == 0 + + +def test_do_not_preprocess_stt_file(snl_wrapper: SNLWrapper): + snl_filename = "test_file" + seq_src = snl_wrapper.module_dir / f"{snl_filename}.stt" + seq_src.write_text( + '#define MESSAGE "waiting\\n"\n' + TEST_SEQ_SRC.format("MESSAGE") + ) + + snl_wrapper.add_var_to_module_makefile("SOURCES", f"{snl_filename}.stt") + + rc, _, errs = snl_wrapper.run_make("cellbuild") + assert rc == 2 + assert ( + f"No rule to make target `{snl_filename}.c', needed by `{snl_filename}_snl.dbd'" + in errs + ) + assert not (snl_wrapper.build_dir / f"{snl_filename}.i").is_file() diff --git a/tests/utils.py b/tests/utils.py index 3a9958dacc22760b0ad059c7eb105ed5e0bb887a..312f9c32a4b9589af446b52b94b28b45441aa70d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -10,42 +10,54 @@ from run_iocsh import IOC class Wrapper: - def __init__(self, root_path: Path, name=None, include_dbd=True, **kwargs): + 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 = ( - Path(os.environ.get("EPICS_BASE")) - / "require" - / os.environ.get("E3_REQUIRE_VERSION") - / "configure" + 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.path = root_path / f"e3-{name}" 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 = ( - name if "E3_MODULE_SRC_PATH" not in kwargs else kwargs["E3_MODULE_SRC_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.module_dir.mkdir(parents=True) + self.build_dir = self.module_dir / f"O.{self.base_version}_{self.host_arch}" - self.config_dir = self.path / "configure" - self.config_dir.mkdir() + self.makefile = self.path / "Makefile" + self.module_makefile = self.path / f"{self.name}.Makefile" - self.config_module = self.config_dir / "CONFIG_MODULE" - self.config_module.touch() + 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() - self.makefile = self.path / "Makefile" - makefile_contents = f""" + makefile_contents = f""" TOP:=$(CURDIR) E3_MODULE_NAME:={name} @@ -61,21 +73,20 @@ REQUIRE_CONFIG:={e3_require_config} include $(REQUIRE_CONFIG)/CONFIG include $(REQUIRE_CONFIG)/RULES_SITEMODS """ - (self.makefile).write_text(makefile_contents) + self.makefile.write_text(makefile_contents) - self.module_makefile = self.path / f"{name}.Makefile" - module_makefile_contents = """ + module_makefile_contents = """ where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) include $(E3_REQUIRE_TOOLS)/driver.makefile EXCLUDE_ARCHS+=debug """ - (self.module_makefile).write_text(module_makefile_contents) + self.module_makefile.write_text(module_makefile_contents) - if include_dbd: - self.add_file("test.dbd") + if include_dbd: + self.add_file("test.dbd") - Repo.init(self.path) + Repo.init(self.path) def add_file(self, name): (self.module_dir / name).touch()