Skip to content

Commit

Permalink
combine ioc compilation functions
Browse files Browse the repository at this point in the history
  • Loading branch information
gilesknap committed Sep 30, 2023
1 parent 4e9712e commit 06a2b62
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 76 deletions.
27 changes: 14 additions & 13 deletions src/ibek/ioc_cmds/assets.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import shutil
import subprocess
from pathlib import Path
Expand All @@ -8,7 +9,7 @@
from ibek.globals import EPICS_ROOT, IOC_FOLDER, SYMLINKS


def get_ioc_src():
def get_ioc_source() -> Path:
"""
The generic ioc source folder is mounted into the container at
/epics/ioc-XXXXX and should always contain the ibek-support
Expand All @@ -22,9 +23,8 @@ def get_ioc_src():
ibek_support = list(EPICS_ROOT.glob("*/ibek-support"))[0]
except IndexError:
raise RuntimeError(
f"Could not find ibek-support in {EPICS_ROOT}."
"ibek should be run from inside a container with"
"a generic ioc source folder mounted at /epics/ioc-XXXXX"
"Could not find a suitable location for the IOC source folder. "
"ibek must be run in a container with the generic IOC source folder"
)
return (ibek_support / "..").resolve()

Expand Down Expand Up @@ -54,23 +54,24 @@ def move_file(src: Path, dest: Path, binary: List[str]):

def extract_assets(destination: Path, source: Path, extras: List[Path], defaults: bool):
"""
extract and copy runtime assets
extract and copy runtime assets from a completed developer stage container
"""
asset_matches = "bin|configure|db|dbd|include|lib|template|config|*.sh"

ibek_support = get_ioc_src()
# chdir out of the folders we will move
os.chdir(source)

just_copy = (
[
ibek_support,
# a default set of assets that all IOCs will need at runtime
if defaults:
default_assets = [
get_ioc_source() / "ibek-support",
source / "support" / "configure",
SYMLINKS,
IOC_FOLDER,
Path("/venv"),
]
if defaults
else []
)
else:
default_assets = []

# identify EPICS modules as folders with binary output folders
binary = ["bin", "lib"]
Expand Down Expand Up @@ -100,7 +101,7 @@ def extract_assets(destination: Path, source: Path, extras: List[Path], defaults
dest_file = destination_module / asset.relative_to(module)
move_file(src, dest_file, binary)

extra_files = just_copy + extras
extra_files = default_assets + extras
for asset in extra_files:
src = source / asset
if src.exists():
Expand Down
123 changes: 66 additions & 57 deletions src/ibek/ioc_cmds/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,66 +17,11 @@
)
from ibek.ioc_cmds.docker import build_dockerfile

from .assets import extract_assets, get_ioc_src
from .assets import extract_assets, get_ioc_source

ioc_cli = typer.Typer(cls=NaturalOrderGroup)


@ioc_cli.command()
def generate_makefile(
folder_override: Annotated[
Optional[Path],
typer.Option(
help="Where to find the Makefile.jinja template.",
),
] = None
):
"""
get the dbd and lib files from all support modules and generate
iocApp/src/Makefile from iocApp/src/Makefile.jinja
"""

# Folder containing Makefile.jinja
make_folder = folder_override or get_ioc_src() / "ioc/iocApp/src"

# get all the dbd and lib files gathered from each support module
dbds: List[str] = []
libs: List[str] = []
if IOC_DBDS.exists():
dbds = [dbd.strip() for dbd in IOC_DBDS.read_text().split()]
if IOC_LIBS.exists():
libs = [lib.strip() for lib in IOC_LIBS.read_text().split()]

# generate the Makefile from the template
template = Template((make_folder / "Makefile.jinja").read_text())
# libraries are listed in reverse order of dependency
libs.reverse()
text = template.render(dbds=dbds, libs=libs)

with (make_folder / "Makefile").open("w") as stream:
stream.write(text)


@ioc_cli.command(
context_settings={"allow_extra_args": True, "ignore_unknown_options": True}
)
def compile(
ctx: typer.Context,
):
"""
Compile a generic IOC after support modules are registered and compiled
"""
path = IOC_FOLDER

# for developer convenience in a devcontainer we also call the idempotent
# functions that compile requires
make_source_template()
generate_makefile()

command = f"make -C {path} -j $(nproc) " + " ".join(ctx.args)
exit(subprocess.call(["bash", "-c", command]))


@ioc_cli.command()
def build(
start: int = typer.Option(1, help="The step to start at in the Dockerfile"),
Expand Down Expand Up @@ -158,15 +103,79 @@ def make_source_template(
Default is ioc source repo / ioc .
"""
if destination is None:
destination = get_ioc_src() / "ioc"
destination = get_ioc_source() / "ioc"

if not destination.exists():
if destination.is_symlink():
destination.unlink()
shutil.copytree(TEMPLATES / "ioc", destination)

# make a symlink to the ioc folder in the root of the epics folder
# this becomes the standard place for code to look for the IOC
try:
IOC_FOLDER.unlink(missing_ok=True)
except IsADirectoryError:
shutil.rmtree(IOC_FOLDER)
IOC_FOLDER.symlink_to(destination)


@ioc_cli.command()
def generate_makefile(
folder_override: Annotated[
Optional[Path],
typer.Option(
help="Where to find the Makefile.jinja template.",
),
] = None,
call_make_source: Annotated[
bool, typer.Option(help="Call make_source_template")
] = False,
):
"""
get the dbd and lib files from all support modules and generate
iocApp/src/Makefile from iocApp/src/Makefile.jinja
"""

if call_make_source:
make_source_template()

# Folder containing Makefile.jinja
make_folder = folder_override or get_ioc_source() / "ioc" / "iocApp" / "src"

# get all the dbd and lib files gathered from each support module
dbds: List[str] = []
libs: List[str] = []
if IOC_DBDS.exists():
dbds = [dbd.strip() for dbd in IOC_DBDS.read_text().split()]
if IOC_LIBS.exists():
libs = [lib.strip() for lib in IOC_LIBS.read_text().split()]

# generate the Makefile from the template
template = Template((make_folder / "Makefile.jinja").read_text())
# libraries are listed in reverse order of dependency
libs.reverse()
text = template.render(dbds=dbds, libs=libs)

with (make_folder / "Makefile").open("w") as stream:
stream.write(text)


@ioc_cli.command(
context_settings={"allow_extra_args": True, "ignore_unknown_options": True}
)
def compile(
ctx: typer.Context,
call_generate_makefile: bool = typer.Option(True, help="Call generate_makefile"),
):
"""
Compile a generic IOC after support modules are registered and compiled
"""
path = IOC_FOLDER

# for developer convenience in we also call the idempotent functions that
# compile depends upon as it is benign to call them multiple times
if call_generate_makefile:
generate_makefile(call_make_source=True)

command = f"make -C {path} -j $(nproc) " + " ".join(ctx.args)
exit(subprocess.call(["bash", "-c", command]))
12 changes: 8 additions & 4 deletions src/ibek/ioc_cmds/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ def expand_env_vars(tokens: List[str]):
return tokens


def handle_command(tokens: List[str], step, start):
def handle_command(context: Path, tokens: List[str], step, start):
msg = f'step {step} {" ".join(tokens)}'
docker_action = tokens[0]
tokens = expand_env_vars(tokens)

print("context,", context)

if step < start and docker_action != "WORKDIR":
print("SKIPPING: " + msg)
return
Expand All @@ -27,12 +29,13 @@ def handle_command(tokens: List[str], step, start):
if result > 0:
raise RuntimeError("RUN command failed")
elif docker_action == "WORKDIR":
folder = Path(tokens[1])
folder = Path.cwd() / Path(tokens[1])
if not folder.exists():
folder.mkdir(parents=True, exist_ok=True)
os.chdir(tokens[1])
elif docker_action == "COPY":
src = (Path(tokens[1])).absolute()
print("COPY", tokens)
src = context / (Path(tokens[1]))
dest = (Path.cwd() / Path(tokens[2])).absolute()
if src == dest:
print("SKIPPING copy of same path")
Expand All @@ -56,6 +59,7 @@ def build_dockerfile(dockerfile: Path, start: int, stop: int):
raise FileNotFoundError(
"No Dockerfile. Run this command in the generic ioc root folder."
)
context = dockerfile.parent

dockerfile_lines: List[str] = dockerfile.read_text().split("\n")

Expand All @@ -70,7 +74,7 @@ def build_dockerfile(dockerfile: Path, start: int, stop: int):
if command == "" or command.startswith("#"):
continue

handle_command(command.split(), step, start)
handle_command(context, command.split(), step, start)
step = step + 1
if step > stop:
break
4 changes: 2 additions & 2 deletions src/ibek/support_cmds/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import typer

from ibek.ioc_cmds.assets import get_ioc_src
from ibek.ioc_cmds.assets import get_ioc_source

try:
from git import Repo
Expand Down Expand Up @@ -243,7 +243,7 @@ def generate_links(
generate symlinks to the bob, pvi and support YAML for a compiled IOC
"""
# symlink the support YAML
from_path = ibek_support or get_ioc_src() / "ibek-support"
from_path = ibek_support or get_ioc_source() / "ibek-support"

support_yaml = from_path.glob("*/*.ibek.support.yaml")

Expand Down

0 comments on commit 06a2b62

Please sign in to comment.