Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 9 additions & 0 deletions Documentation/config-yaml.rst
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@ Flash command can use special tags that are handled by NTFC:
- ``$IMAGE_BIN`` is replaced by path to ``nuttx.bin``.
- ``$IMAGE_HEX`` is replaced by path to ``nuttx.hex``.
- ``$IMAGE_ELF`` is replaced by path to the core image (``elf_path``).
- ``$APPS_BINDIR`` is replaced by the application binaries directory
(kernel-mode builds).
- ``$APPS_IMG`` is replaced by the generated application filesystem
image (requires ``apps_image``).

Example usage with ``st-flash`` tool:

Expand Down Expand Up @@ -466,6 +470,11 @@ These fields are parsed by :class:`ntfc.coreconfig.CoreConfig`.
- (Optional) Directory with kernel-mode application binaries. Defaults
to the ``bin/`` directory next to the NuttX ELF for kernel-mode
builds (``CONFIG_BUILD_KERNEL=y``)
* - ``apps_image``
- (Optional) Generate a filesystem image with the application
binaries after build, e.g. ``apps_image: {type: romfs}``. The
image path is available as ``$APPS_IMG`` in the ``flash``
command. Requires ``genromfs`` and a kernel-mode build
* - ``defconfig``
- Path to NuttX defconfig (auto-build)
* - ``elf_path``
Expand Down
3 changes: 3 additions & 0 deletions Documentation/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ product: # many products can be supported in tests (pro
app_bindir: '' # (optional) directory with kernel-mode application binaries.
# Defaults to the bin/ directory next to the NuttX ELF
# for kernel-mode builds.
apps_image: # (optional) generate a filesystem image with the application
type: romfs # binaries after build (kernel-mode only, requires genromfs).
# The image path is available as $APPS_IMG in 'flash'.

# NTFC can use pre-build image or build it from defconfig
# the behavior will depend on the parameters specified in config.
Expand Down
106 changes: 92 additions & 14 deletions src/ntfc/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import re
import shutil
import subprocess
from functools import partial
from pathlib import Path
from typing import Any, Dict, List, Optional

Expand All @@ -40,6 +41,9 @@ class NuttXBuilder:
IMAGE_BIN_STR = "$IMAGE_BIN"
IMAGE_HEX_STR = "$IMAGE_HEX"
IMAGE_ELF_STR = "$IMAGE_ELF"
APPS_BINDIR_STR = "$APPS_BINDIR"
APPS_IMG_STR = "$APPS_IMG"
APPS_IMG_NAME = "apps.romfs.img"
_KCONFIG_DISABLED_RE = re.compile(
r"^#\s+(CONFIG_[A-Za-z0-9_]+)\s+is not set"
)
Expand Down Expand Up @@ -348,7 +352,6 @@ def _run_build(
"--build",
str(build_path),
]

run_env = os.environ.copy()
if env:
run_env.update(env) # pragma: no cover
Expand Down Expand Up @@ -428,36 +431,117 @@ def _build_core(
if not already_build or self._rebuild: # pragma: no cover
self._log_kconfig_overrides(kv_overrides)

# configure build
self._run_cmake(
configure = partial(
self._run_cmake,
source=nuttx_dir,
build=build_path,
generator="Ninja",
defines=defines,
env=build_env,
)

# configure build
configure()

# apply Kconfig overrides to generated .config before build
self._apply_kconfig_overrides(
nuttx_conf_path, kv_overrides, cfg_cwd
)

# Regenerate include/nuttx/config.h after changing .config.
# Otherwise CMake can relink an image built with stale Kconfig
# values while the saved .config claims the override applied.

if kv_overrides:
# fill in defaults of suboptions the overrides
# unlocked (e.g. *_PROGNAME): applications are
# silently dropped at configure when these are
# missing from .config
self._run_build_target(
build_path, "olddefconfig", env=build_env
)

# application targets are registered at configure
# time from .config: configure again so overrides
# that enable new applications take effect
configure()

# build
self._run_build(build_path, env=build_env)

# add elf and conf path
cores[core]["elf_path"] = nuttx_elf_path
cores[core]["conf_path"] = nuttx_conf_path

if self._is_kernel_config(nuttx_conf_path):
# kernel-mode: applications are installed to <build>/bin
# and hostfs mounts resolve relative to the process cwd
cores[core].setdefault(
"app_bindir", os.path.join(build_path, "bin")
)
cores[core].setdefault("exec_cwd", build_path)

self._make_apps_image(cores[core], build_path)

def _make_apps_image(
self, core_cfg: Dict[str, Any], build_path: str
) -> None:
"""Generate a filesystem image with application binaries.

Enabled with the per-core ``apps_image`` option; the image path
is registered as ``apps_img`` for the ``$APPS_IMG`` flash
placeholder.
"""
img_cfg = core_cfg.get("apps_image", None)
if not img_cfg:
return

if not isinstance(img_cfg, dict):
raise BuilderConfigError("apps_image must be a mapping")

img_type = img_cfg.get("type", "romfs")
if img_type != "romfs":
raise BuilderConfigError(
f"unsupported apps_image type: {img_type}"
)

bindir = core_cfg.get("app_bindir", None)
if not bindir:
raise BuilderConfigError(
"apps_image requires app_bindir (kernel-mode build)"
)

tool = shutil.which("genromfs")
if not tool:
raise BuilderConfigError("genromfs not found in PATH")

img_path = os.path.join(build_path, self.APPS_IMG_NAME)
self._run_command([tool, "-f", img_path, "-d", bindir], env=None)
core_cfg["apps_img"] = img_path

def _expand_flash_cmd(
self, flash_cmd: str, core_cfg: Dict[str, Any]
) -> str:
"""Expand image placeholders in a flash command."""
parent = Path(core_cfg["elf_path"]).parent
values = {
self.IMAGE_BIN_STR: str(parent / "nuttx.bin"),
self.IMAGE_HEX_STR: str(parent / "nuttx.hex"),
self.IMAGE_ELF_STR: core_cfg["elf_path"],
self.APPS_BINDIR_STR: core_cfg.get("app_bindir", ""),
self.APPS_IMG_STR: core_cfg.get("apps_img", ""),
}

for placeholder, value in values.items():
flash_cmd = flash_cmd.replace(placeholder, value)

return flash_cmd

@staticmethod
def _is_kernel_config(conf_path: str) -> bool:
"""Check if a generated .config selects a kernel build."""
if not os.path.isfile(conf_path):
return False

with open(conf_path, "r", encoding="utf-8") as f:
return any(line.strip() == "CONFIG_BUILD_KERNEL=y" for line in f)

def _reboot_core(
self, core: str, cores: Dict[str, Any]
) -> None: # pragma: no cover
Expand All @@ -474,13 +558,7 @@ def _flash_core(
"""Flash single core image."""
flash_cmd = cores[core].get("flash", None)
if flash_cmd:
img_path = Path(cores[core]["elf_path"])
image_hex = str(img_path.parent) + "/nuttx.hex"
image_bin = str(img_path.parent) + "/nuttx.bin"

flash_cmd = flash_cmd.replace(self.IMAGE_BIN_STR, image_bin)
flash_cmd = flash_cmd.replace(self.IMAGE_HEX_STR, image_hex)
flash_cmd = flash_cmd.replace(self.IMAGE_ELF_STR, str(img_path))
flash_cmd = self._expand_flash_cmd(flash_cmd, cores[core])

cmd = flash_cmd.split()

Expand Down
Loading