diff --git a/.vscode/settings.json b/.vscode/settings.json index 10c2c2b6f..fa4113683 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,5 +18,10 @@ "source.organizeImports.ruff": true, "source.black": true, } + }, + "workbench.colorCustomizations": { + "activityBar.background": "#01333D", + "titleBar.activeBackground": "#014756", + "titleBar.activeForeground": "#F1FDFF" } } \ No newline at end of file diff --git a/config/st.cmd b/config/st.cmd deleted file mode 100644 index 825100d4c..000000000 --- a/config/st.cmd +++ /dev/null @@ -1,10 +0,0 @@ -# EPICS IOC Startup Script generated by https://github.com/epics-containers/ibek - -cd "/epics/ioc" -dbLoadDatabase dbd/ioc.dbd -ioc_registerRecordDeviceDriver pdbbase - - -dbLoadRecords /tmp/ioc.db -iocInit - diff --git a/src/ibek/globals.py b/src/ibek/globals.py index fc7ce3efa..a9c4b279f 100644 --- a/src/ibek/globals.py +++ b/src/ibek/globals.py @@ -25,8 +25,6 @@ # global MODULES file used to determine order of build MODULES = SUPPORT / "configure/MODULES" -# Folder containing Makefile.jinja -MAKE_FOLDER = IOC_FOLDER / "iocApp/src" # Folder containing templates for IOC src etc. TEMPLATES = Path(__file__).parent / "templates" # Folder containing symlinks to useful files diff --git a/src/ibek/ioc_cmds/assets.py b/src/ibek/ioc_cmds/assets.py index 4fd75189b..2bda8d228 100644 --- a/src/ibek/ioc_cmds/assets.py +++ b/src/ibek/ioc_cmds/assets.py @@ -8,6 +8,27 @@ from ibek.globals import EPICS_ROOT, IOC_FOLDER, SYMLINKS +def get_ioc_src(): + """ + The generic ioc source folder is mounted into the container at + /epics/ioc-XXXXX and should always contain the ibek-support + submodule. Therefore we can find the ibek-support folder by looking + for the ibek-support folder. + + Functions that use this should provide an override variable that allows + the ibek caller to specify the location. + """ + try: + 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" + ) + return (ibek_support / "..").resolve() + + def move_file(src: Path, dest: Path, binary: List[str]): """ Move a file / tree / symlink from src to dest, stripping symbols from @@ -37,10 +58,7 @@ def extract_assets(destination: Path, source: Path, extras: List[Path], defaults """ asset_matches = "bin|configure|db|dbd|include|lib|template|config|*.sh" - try: - ibek_support = list(EPICS_ROOT.glob("*/ibek-support"))[0] - except IndexError: - raise RuntimeError(f"Could not find ibek-support in {EPICS_ROOT}") + ibek_support = get_ioc_src() just_copy = ( [ diff --git a/src/ibek/ioc_cmds/commands.py b/src/ibek/ioc_cmds/commands.py index 7ccfa4ce4..272b3930c 100644 --- a/src/ibek/ioc_cmds/commands.py +++ b/src/ibek/ioc_cmds/commands.py @@ -12,23 +12,33 @@ IOC_DBDS, IOC_FOLDER, IOC_LIBS, - MAKE_FOLDER, TEMPLATES, NaturalOrderGroup, ) from ibek.ioc_cmds.docker import build_dockerfile -from .assets import extract_assets +from .assets import extract_assets, get_ioc_src ioc_cli = typer.Typer(cls=NaturalOrderGroup) @ioc_cli.command() -def generate_makefile() -> None: +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] = [] @@ -38,12 +48,12 @@ def generate_makefile() -> None: 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()) + 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: + with (make_folder / "Makefile").open("w") as stream: stream.write(text) @@ -58,6 +68,11 @@ def compile( """ 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])) @@ -100,7 +115,7 @@ def generate_schema( ioc_model = ioc_create_model(definitions) schema = json.dumps(ioc_model.model_json_schema(), indent=2) if output is None: - print(schema) + typer.echo(schema) else: output.write_text(schema) @@ -130,22 +145,25 @@ def extract_runtime_assets( @ioc_cli.command() def make_source_template( - destination: Path = typer.Argument( - Path.cwd().parent / "ioc", - help="The root folder in which to place the IOC boilerplate", - ) + destination: Annotated[ + Optional[Path], + typer.Option( + help="Where to make the ioc folder. Defaults to under the " + "generic IOC source folder", + ), + ] = None ): """ Create a new IOC boilerplate source tree in the given folder. - Default is ../ioc. Typically call this when CWD is - /ibek-support as this is the standard - Dockerfile WORKDIR. + Default is ioc source repo / ioc . """ - if destination.exists(): - typer.echo(f"{destination} IOC source exists, skipping ...") - else: + if destination is None: + destination = get_ioc_src() / "ioc" + + if not destination.exists(): + if destination.is_symlink(): + destination.unlink() shutil.copytree(TEMPLATES / "ioc", destination) - typer.echo(f"Created IOC source tree in {destination}") try: IOC_FOLDER.unlink(missing_ok=True) diff --git a/src/ibek/support_cmds/commands.py b/src/ibek/support_cmds/commands.py index e75146634..945d4b607 100644 --- a/src/ibek/support_cmds/commands.py +++ b/src/ibek/support_cmds/commands.py @@ -7,10 +7,12 @@ import typer +from ibek.ioc_cmds.assets import get_ioc_src + try: from git import Repo except ImportError: - print("Git Python is not needed for runtime ibek usage") + pass # Git Python is not needed for runtime (container build time only) from typing_extensions import Annotated @@ -229,24 +231,29 @@ def compile( @support_cli.command() def generate_links( - module: str = typer.Argument(..., help="support module name"), ibek_support: Annotated[ - Path, typer.Option(help="Filepath to ibek-support root") - ] = Path.cwd(), + Optional[Path], + typer.Option( + help="Filepath to ibek-support root" + "defaults to /ibek-support" + ), + ] = None, ): """ generate symlinks to the bob, pvi and support YAML for a compiled IOC """ # symlink the support YAML - from_path = ibek_support / module - support_yaml = from_path.glob("*.ibek.support.yaml") - print(f"looking for some support YAML in {ibek_support}, found {support_yaml}") + from_path = ibek_support or get_ioc_src() / "ibek-support" + + support_yaml = from_path.glob("*/*.ibek.support.yaml") to_path = SYMLINKS / "ibek" to_path.mkdir(parents=True, exist_ok=True) for yaml in support_yaml: + link_from = to_path / yaml.name + link_from.unlink(missing_ok=True) typer.echo(f"symlinking {yaml} to {to_path}") - (to_path / yaml.name).symlink_to(yaml) + link_from.symlink_to(yaml) # symlink the bob YAML # TODO TODO @@ -261,6 +268,6 @@ def generate_schema( ): """Produce JSON global schema for all .ibek.support.yaml files""" if output is None: - print(Support.get_schema()) + typer.echo(Support.get_schema()) else: output.write_text(Support.get_schema()) diff --git a/src/ibek/templates/ioc/config/start.sh b/src/ibek/templates/ioc/config/start.sh index 3b0632d53..8961f3b4d 100644 --- a/src/ibek/templates/ioc/config/start.sh +++ b/src/ibek/templates/ioc/config/start.sh @@ -8,22 +8,22 @@ Launch this Generic IOC with configuration folder mounted over Choices of contents of the config folder are: - 1. start.sh ****************************************************************** - If the config folder contains a start.sh script it will be executed. - This allows the instance implementer to provide a conmpletely custom - startup script. - - 2. ioc.yaml ************************************************************* + 1. ioc.yaml ************************************************************* If the config folder contains an ioc.yaml file we invoke the ibek tool to generate the startup script and database. Then launch with the generated startup script. - 3. st.cmd + ioc.subst ********************************************************* + 2. st.cmd + ioc.subst ********************************************************* If the config folder contains a st.cmd script and a ioc.subst file then optionally generate ioc.db from the ioc.subst file and use the st.cmd script as the IOC startup script. Note that the expanded database file will be generated in /tmp/ioc.db + 3. start.sh ****************************************************************** + If the config folder contains a start.sh script it will be executed. + This allows the instance implementer to provide a conmpletely custom + startup script. + 4. empty config folder ******************************************************* If the config folder is empty the default startup script prints the message you are reading now. diff --git a/src/ibek/templates/ioc/start.sh b/src/ibek/templates/ioc/start.sh index 7db7bfbc4..c20c1bb33 100755 --- a/src/ibek/templates/ioc/start.sh +++ b/src/ibek/templates/ioc/start.sh @@ -15,25 +15,25 @@ # Here we support the following set of options for the contents of # the config folder: # -# 1. start.sh ****************************************************************** -# If the config folder contains a start.sh script it will be executed. -# This allows the instance implementer to provide a conmpletely custom -# startup script. -# -# 2. ioc.yaml ************************************************************* +# 1. ioc.yaml ************************************************************* # If the config folder contains an ioc.yaml file we invoke the ibek tool to # generate the startup script and database. Then launch with the generated # startup script. # -# 3. st.cmd + ioc.subst ********************************************************* +# 2. st.cmd + ioc.subst ********************************************************* # If the config folder contains a st.cmd script and a ioc.subst file then # optionally generate ioc.db from the ioc.subst file and use the st.cmd script # as the IOC startup script. Note that the expanded database file will # be generated in /tmp/ioc.db # +# 3. start.sh ****************************************************************** +# If the config folder contains a start.sh script it will be executed. +# This allows the instance implementer to provide a conmpletely custom +# startup script. +# # 4. empty config folder ******************************************************* -# If the config folder is empty then this IOC will launch the example in -# ./example folder +# If the config folder is empty the default script that prints a message +# will run # # RTEMS IOCS - RTEMS IOC startup files can be generated using 2,3,4 above. For # RTEMS we do not execute the ioc inside of the pod. Instead we: @@ -65,42 +65,36 @@ ioc_startup=${CONFIG_DIR}/st.cmd epics_db=/tmp/ioc.db -# 1. start.sh ****************************************************************** -if [ -f ${override} ]; then - exec bash ${override} - - elif [ -f ${ibek_src} ]; then - # 2. ioc.yaml ****************************************************************** - +# 1. ioc.yaml ****************************************************************** +if [ -f ${ibek_src} ]; then + # Database generation script generated by ibek db_src=/tmp/ioc.subst final_ioc_startup=/tmp/st.cmd - + # get ibek the support yaml files this ioc's support modules - defs=/workspaces/*/ibek-support/*/*.ibek.support.yaml + defs=/epics/links/ibek/*.ibek.support.yaml ibek startup generate ${ibek_src} ${defs} --out ${final_ioc_startup} --db-out ${db_src} - + # build expanded database using msi if [ -f ${db_src} ]; then includes=$(for i in ${SUPPORT}/*/db; do echo -n "-I $i "; done) bash -c "msi -o${epics_db} ${includes} -S${db_src}" fi - - # 3. st.cmd + ioc.substitutions ************************************************ - - elif [ -f ${ioc_startup} ] ; then + +# 2. st.cmd + ioc.substitutions ************************************************ +elif [ -f ${ioc_startup} ] ; then + if [ -f ${CONFIG_DIR}/ioc.substitutions ]; then # generate ioc.db from ioc.substitutions, including all templates from SUPPORT includes=$(for i in ${SUPPORT}/*/db; do echo -n "-I $i "; done) msi ${includes} -S ${CONFIG_DIR}/ioc.substitutions -o ${epics_db} fi final_ioc_startup=${ioc_startup} - - # 4. empty config folder ******************************************************* - -else - final_ioc_startup=${TOP}/example/st.cmd + +elif [ -f ${override} ]; then + exec bash ${override} fi # Launch the IOC ***************************************************************