Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework the Globals Paths and their use in tests #219

Merged
merged 3 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/ibek/dev_cmds/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import typer

from ibek.globals import CONFIG_DIR_NAME, IOC_FOLDER, NaturalOrderGroup
from ibek.globals import GLOBALS, NaturalOrderGroup

log = logging.getLogger(__name__)
dev_cli = typer.Typer(cls=NaturalOrderGroup)
Expand Down Expand Up @@ -34,9 +34,9 @@ def instance(
"""

# validate the instance folder has a config folder
ioc_folder = IOC_FOLDER
config_folder = ioc_folder / CONFIG_DIR_NAME
instance_config = instance / CONFIG_DIR_NAME
ioc_folder = GLOBALS.IOC_FOLDER
config_folder = ioc_folder / GLOBALS.CONFIG_DIR_NAME
instance_config = instance / GLOBALS.CONFIG_DIR_NAME

# verify that the expected folder exists
if not ioc_folder.exists():
Expand Down
141 changes: 98 additions & 43 deletions src/ibek/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,77 +12,132 @@


class _Globals:
"""Helper class for accessing global constants."""
"""
Helper class for accessing global constants.

def __init__(self) -> None:
self.EPICS_ROOT = Path(os.getenv("EPICS_ROOT", "/epics/"))
"""
Root of epics directory tree.
These constants define the paths to the various directories used by the
ibek commands.
"""

Can be overridden by defining an environment variable "EPICS_ROOT".
"""
def __init__(self) -> None:
"""Initialize the global constants."""

self.SUPPORT = self.EPICS_ROOT / "support"
"""
Directory containing support module clones
# Can be overridden by defining an environment variable "EPICS_ROOT"
self._EPICS_ROOT = Path(os.getenv("EPICS_ROOT", "/epics/"))

Can be overridden by defining an environment variable "SUPPORT"."
"""
self._DEFAULT_ARCH = "linux-x86_64"

self.RELEASE = self.EPICS_ROOT / "support" / "configure" / "RELEASE"
"""The global RELEASE file which lists all support modules"""
@property
def EPICS_ROOT(self):
"""Root of epics directory tree"""
return self._EPICS_ROOT

self.IBEK_DEFS = self.EPICS_ROOT / "ibek-defs"
"""Directory containing ibek support yaml definitions."""
@property
def SUPPORT(self):
"""Directory containing support module clones"""
return self._EPICS_ROOT / "support"

self.PVI_DEFS = self.EPICS_ROOT / "pvi-defs"
"""Directory containing pvi device yaml definitions."""
@property
def RELEASE(self):
"""The global RELEASE file which lists all support modules"""
return self._EPICS_ROOT / "support" / "configure" / "RELEASE"

self.RUNTIME_OUTPUT = self.EPICS_ROOT / "runtime"
@property
def RUNTIME_OUTPUT(self):
"""Directory containing runtime generated assets for IOC boot."""
return self._EPICS_ROOT / "runtime"

self.OPI_OUTPUT = self.EPICS_ROOT / "opi"
"""Directory containing runtime generated opis to serve over http."""

self.EPICS_TARGET_ARCH = os.getenv("EPICS_TARGET_ARCH", DEFAULT_ARCH)
@property
def EPICS_TARGET_ARCH(self):
"""The target architecture for the current container."""
return os.getenv("EPICS_TARGET_ARCH", self._DEFAULT_ARCH)

self.EPICS_HOST_ARCH = os.getenv("EPICS_HOST_ARCH", DEFAULT_ARCH)
@property
def EPICS_HOST_ARCH(self):
"""The host architecture for the current container."""
return os.getenv("EPICS_HOST_ARCH", self._DEFAULT_ARCH)

self.NATIVE = self.EPICS_TARGET_ARCH == self.EPICS_HOST_ARCH
@property
def NATIVE(self):
"""True if the target architecture is the same as the host architecture."""
return self.EPICS_TARGET_ARCH == self.EPICS_HOST_ARCH

default_static: bool = self.EPICS_TARGET_ARCH != DEFAULT_ARCH
self.STATIC_BUILD = os.getenv("STATIC_BUILD", default_static)

@property
def STATIC_BUILD(self):
"""True if the target architecture is not the default architecture."""
return os.getenv("STATIC_BUILD", self._EPICS_TARGET_ARCH != self._DEFAULT_ARCH)

GLOBALS = _Globals()
@property
def IBEK_DEFS(self):
"""Directory containing ibek support yaml definitions."""
return self._EPICS_ROOT / "ibek-defs"

# TODO: Include all constants in _Globals
@property
def PVI_DEFS(self):
"""Directory containing pvi device yaml definitions."""
return self._EPICS_ROOT / "pvi-defs"
return self._EPICS_ROOT / "pvi-defs"
gilesknap marked this conversation as resolved.
Show resolved Hide resolved

# get the container paths from environment variables
EPICS_BASE = Path(os.getenv("EPICS_BASE", "/epics/epics-base"))
IOC_FOLDER = Path(os.getenv("IOC", "/epics/ioc"))
CONFIG_DIR_NAME = "config"
IOC_DIR_NAME = "ioc"
@property
def OPI_OUTPUT(self):
"""Directory containing runtime generated opis to serve over http."""
return self._EPICS_ROOT / "opi"

@property
def EPICS_BASE(self):
"""xx"""
gilesknap marked this conversation as resolved.
Show resolved Hide resolved
return self._EPICS_ROOT / "epics-base"

@property
def IOC_FOLDER(self):
"""root folder of a generic IOC source inside the container"""
return self._EPICS_ROOT / "ioc"

@property
def CONFIG_DIR_NAME(self):
"""configuration directory name for the IOC"""
gilesknap marked this conversation as resolved.
Show resolved Hide resolved
return "config"

@property
def IOC_DIR_NAME(self):
"""folder of the IOC source"""
return "ioc"

@property
def RELEASE_SH(self):
"""a bash script to export the macros defined in RELEASE as environment vars"""
return self.SUPPORT / "configure" / "RELEASE.shell"

@property
def MODULES(self):
"""global MODULES file used to determine order of build"""
return self.SUPPORT / "configure" / "MODULES"

@property
def IOC_DBDS(self):
"""ibek-support list of declared dbds"""
return self.SUPPORT / "configure" / "dbd_list"

@property
def IOC_LIBS(self):
"""ibek-support list of declared libs"""
return self.SUPPORT / "configure" / "lib_list"

@property
def RUNTIME_DEBS(self):
"""ibek-support list of declared libs"""
return self.SUPPORT / "configure" / "runtime_debs"

# a bash script to export the macros defined in RELEASE as environment vars
RELEASE_SH = GLOBALS.SUPPORT / "configure/RELEASE.shell"
# global MODULES file used to determine order of build
MODULES = GLOBALS.SUPPORT / "configure/MODULES"

# Folder containing templates for IOC src etc.
TEMPLATES = Path(__file__).parent / "templates"

# Paths for ibek-support
# Path suffixes for ibek-support
IBEK_GLOBALS = Path("_global")
SUPPORT_YAML_PATTERN = "*ibek.support.yaml"
PVI_YAML_PATTERN = "*pvi.device.yaml"

IOC_DBDS = GLOBALS.SUPPORT / "configure/dbd_list"
IOC_LIBS = GLOBALS.SUPPORT / "configure/lib_list"
RUNTIME_DEBS = GLOBALS.SUPPORT / "configure/runtime_debs"
GLOBALS = _Globals()


class BaseSettings(BaseModel):
Expand Down
6 changes: 3 additions & 3 deletions src/ibek/ioc_cmds/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import typer

from ibek.globals import GLOBALS, IOC_FOLDER
from ibek.globals import GLOBALS

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -62,9 +62,9 @@ def extract_assets(
source / "support" / "configure",
GLOBALS.PVI_DEFS,
GLOBALS.IBEK_DEFS,
IOC_FOLDER, # get the IOC folder symlink
GLOBALS.IOC_FOLDER, # get the IOC folder symlink
Path.readlink(
IOC_FOLDER
GLOBALS.IOC_FOLDER
).parent, # get contents of IOC folder and its source (parent)
Path("/venv"), # get the virtualenv
] + list(
Expand Down
3 changes: 2 additions & 1 deletion src/ibek/ioc_cmds/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ def build_docker(
help="The filepath to the Dockerfile to build",
autocompletion=lambda: [], # Forces path autocompletion
),
] = Path.cwd() / "Dockerfile",
] = Path.cwd()
/ "Dockerfile",
gilesknap marked this conversation as resolved.
Show resolved Hide resolved
):
"""
EXPERIMENTAL: Attempt to interpret the Dockerfile and run it's commands
Expand Down
6 changes: 3 additions & 3 deletions src/ibek/support_cmds/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import typer

from ibek.globals import GLOBALS, MODULES, RELEASE_SH, TEMPLATES
from ibek.globals import GLOBALS, TEMPLATES

# turn RELEASE macros into bash macros
SHELL_FIND = re.compile(r"\$\(([^\)]*)\)")
Expand Down Expand Up @@ -62,7 +62,7 @@ def do_dependencies():
if "IOC" in global_release_paths:
paths.append(global_release_paths["IOC"])
mod_list = f'MODULES := {" ".join(paths)}\n'
MODULES.write_text(mod_list)
GLOBALS.MODULES.write_text(mod_list)

# generate RELEASE.shell file for inclusion into the ioc launch shell script.
# This adds all module paths to the environment and also adds their db
Expand All @@ -79,7 +79,7 @@ def do_dependencies():

shell_text = "\n".join(release_sh) + "\n"
shell_text = SHELL_FIND.sub(SHELL_REPLACE, shell_text)
RELEASE_SH.write_text(shell_text)
GLOBALS.RELEASE_SH.write_text(shell_text)


def check_deps(deps: list[str]) -> None:
Expand Down
12 changes: 4 additions & 8 deletions src/ibek/support_cmds/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@
from ibek.globals import (
GLOBALS,
IBEK_GLOBALS,
IOC_DBDS,
IOC_LIBS,
PVI_YAML_PATTERN,
RUNTIME_DEBS,
SUPPORT_YAML_PATTERN,
NaturalOrderGroup,
)
Expand Down Expand Up @@ -113,7 +110,7 @@ def add_runtime_packages(
The list may include any additional 'apt-get install' options required.
"""
debs = debs or []
add_list_to_file(RUNTIME_DEBS, debs)
add_list_to_file(GLOBALS.RUNTIME_DEBS, debs)


@support_cli.command()
Expand All @@ -129,8 +126,8 @@ def apt_install_runtime_packages(
print("skipping runtime install in cross-compile environment")
return

if RUNTIME_DEBS.exists():
debs = RUNTIME_DEBS.read_text().split()
if GLOBALS.RUNTIME_DEBS.exists():
debs = GLOBALS.RUNTIME_DEBS.read_text().split()
_install_debs(debs)


Expand Down Expand Up @@ -211,7 +208,7 @@ def add_libs(
declare the libraries for this support module for inclusion in IOC Makefile
"""
libs = libs or []
add_list_to_file(IOC_LIBS, libs)
add_list_to_file(GLOBALS.IOC_LIBS, libs)


@support_cli.command()
Expand All @@ -222,7 +219,6 @@ def add_dbds(
declare the dbd files for this support module for inclusion in IOC Makefile
"""
dbds = dbds or []
add_list_to_file(IOC_DBDS, dbds)


@support_cli.command()
Expand Down
34 changes: 25 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import os
import shutil
from pathlib import Path

from pytest import fixture
from pytest_mock import MockerFixture
from ruamel.yaml import YAML
from typer.testing import CliRunner

from ibek.__main__ import cli
from ibek.entity_factory import EntityFactory

# This must be above the following imports so that it takes effect before
# `globals.EPICS_ROOT` is imported (or anything built on top of it)
os.environ["EPICS_ROOT"] = str(Path(__file__).parent / "samples" / "epics")
os.environ["SUPPORT"] = str(Path(__file__).parent / "samples" / "epics" / "support")

# The `noqa`s on these imports are necessary because of the above
from ibek.__main__ import cli # noqa: E402
from ibek.support import Support # noqa: E402
from ibek.globals import GLOBALS
from ibek.support import Support

runner = CliRunner()

Expand Down Expand Up @@ -63,6 +59,26 @@ def entity_factory():
return EntityFactory()


@fixture
def epics_root(samples: Path, tmp_path: Path, mocker: MockerFixture):
gilesknap marked this conversation as resolved.
Show resolved Hide resolved
# create an partially populated epics_root structure in a temporary folder
epics = tmp_path / "epics"
shutil.copytree(samples / "epics", epics)
gilesknap marked this conversation as resolved.
Show resolved Hide resolved
Path.mkdir(epics / "opi", exist_ok=True)
Path.mkdir(epics / "epics-base")
Path.mkdir(epics / "ioc/config", parents=True)
Path.mkdir(epics / "ibek-defs")
Path.mkdir(epics / "runtime")

mocker.patch.object(GLOBALS, "_EPICS_ROOT", epics)

# this should not be needed - what gives?
os.environ["IOC"] = "/epics/ioc"
os.environ["RUNTIME_DIR"] = "/epics/runtime"

return epics
gilesknap marked this conversation as resolved.
Show resolved Hide resolved


@fixture
def asyn_classes(support_defs, entity_factory):
asyn_support = get_support(support_defs / "asyn.ibek.support.yaml")
Expand Down
Loading
Loading