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
13 changes: 10 additions & 3 deletions ebuild/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,14 @@ def _install_packages(
fetcher.fetch(recipe, cache.src_dir(recipe))

log.step(f" Building {recipe.name} v{recipe.version}...")
dep_dirs = [install_dirs[dep] for dep in recipe.dependencies if dep in install_dirs]
dep_dirs = []
for dep in recipe.dependencies:
if dep not in install_dirs:
raise BuildError(
f"Dependency '{dep}' of '{recipe.name}' was not built. "
"Check that all recipes are available."
)
dep_dirs.append(install_dirs[dep])
install_dir = builder.build(recipe, dep_install_dirs=dep_dirs)
install_dirs[recipe.name] = install_dir
log.success(f" {recipe.name} v{recipe.version} — built ✓")
Expand Down Expand Up @@ -261,12 +268,12 @@ def _run_cmake_build(profile, board, source_dir, build_dir, log):
# Point cmake to generated config headers
gen_include = build_dir / "include" / "generated"
if gen_include.exists():
cmake_defines["EOS_GENERATED_INCLUDE_DIR"] = str(gen_include).replace("\\", "/")
cmake_defines["EOS_GENERATED_INCLUDE_DIR"] = gen_include.as_posix()

# Point to eboot cmake defs if present
eboot_cmake = build_dir / "configs" / "eboot_config.cmake"
if eboot_cmake.exists():
cmake_defines["EBOOT_CONFIG_FILE"] = str(eboot_cmake).replace("\\", "/")
cmake_defines["EBOOT_CONFIG_FILE"] = eboot_cmake.as_posix()

log.step("[6/6] Building with cmake...")
log.info(" Defines: " + str(len(cmake_defines)) + " cmake variables")
Expand Down
13 changes: 0 additions & 13 deletions ebuild/core/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,6 @@ def topological_sort(self) -> List[str]:
Uses Kahn's algorithm. Raises CycleError if a cycle exists.
"""
in_degree: Dict[str, int] = {n: 0 for n in self._nodes}
for node, deps in self._adj.items():
for dep in deps:
in_degree.setdefault(node, 0)
in_degree.setdefault(dep, 0)

# in_degree[x] = number of nodes that depend on x?
# Actually we want: in_degree[x] = number of dependencies x has
# For Kahn's we need: in_degree[x] = number of edges pointing INTO x
# Edge: dependent -> dependency means "dependent needs dependency"
# For build order, dependency must come first.
# Re-interpret: edges as dependency -> dependent (reverse adj for Kahn's)

in_degree = {n: 0 for n in self._nodes}
reverse_adj: Dict[str, Set[str]] = defaultdict(set)

for dependent, deps in self._adj.items():
Expand Down
33 changes: 28 additions & 5 deletions ebuild/eos_ai/eos_config_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@
from ebuild.eos_ai.eos_hw_analyzer import HardwareProfile


_FLASH_BASE_MAP = {
"nrf": "0x00000000",
"rp2040": "0x10000000",
"esp32": "0x00000000",
}


def _flash_base_for(profile: "HardwareProfile") -> str:
"""Return the correct flash base address for this MCU."""
key = (profile.mcu or "").lower()
family = (profile.mcu_family or "").lower()
for prefix, base in _FLASH_BASE_MAP.items():
if key.startswith(prefix) or family.startswith(prefix):
return base
return "0x08000000"


class EosConfigGenerator:
"""Generates build/boot/OS configs from a HardwareProfile."""

Expand Down Expand Up @@ -67,7 +84,7 @@ def generate_board_yaml(self, profile: HardwareProfile) -> Path:
def generate_boot_yaml(self, profile: HardwareProfile) -> Path:
"""Generate eboot-compatible boot configuration."""
flash = profile.flash_size or 1024 * 1024
stage0_size = min(16 * 1024, flash // 32)
stage0_size = max(8 * 1024, min(16 * 1024, flash // 32))
stage1_size = min(64 * 1024, flash // 8)
bootctl_size = 4096
slot_size = (flash - stage0_size - stage1_size - bootctl_size * 2) // 2
Expand All @@ -76,7 +93,7 @@ def generate_boot_yaml(self, profile: HardwareProfile) -> Path:
"boot": {
"board": profile.mcu.lower() or "custom",
"arch": profile.arch,
"flash_base": "0x08000000",
"flash_base": _flash_base_for(profile),
"flash_size": flash,
"layout": {
"stage0": {"offset": "0x00000000", "size": stage0_size},
Expand Down Expand Up @@ -118,9 +135,15 @@ def generate_boot_yaml(self, profile: HardwareProfile) -> Path:

def generate_build_yaml(self, profile: HardwareProfile) -> Path:
"""Generate ebuild build.yaml for the project."""
toolchain = "arm-none-eabi" if profile.arch == "arm" else "gcc"
if profile.has_peripheral("ble"):
pass
_TOOLCHAIN_PREFIX = {
"arm": "arm-none-eabi",
"arm64": "aarch64-linux-gnu",
"aarch64": "aarch64-linux-gnu",
"riscv64": "riscv64-linux-gnu",
"x86_64": "x86_64-linux-gnu",
"mips": "mipsel-linux-gnu",
}
toolchain = _TOOLCHAIN_PREFIX.get(profile.arch, "gcc")

build = {
"project": {
Expand Down
5 changes: 4 additions & 1 deletion ebuild/system/rootfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ def configure_users(
passwd_lines.append(f"{name}:x:{uid}:{uid}:{name}:{home}:{shell}\n")
group_lines.append(f"{name}:x:{uid}:\n")
shadow_lines.append(f"{name}::0:0:99999:7:::\n")
(self.rootfs_dir / home.lstrip("/")).mkdir(parents=True, exist_ok=True)
safe_home = os.path.normpath(home.lstrip("/"))
if ".." in safe_home.split(os.sep):
raise RootfsError(f"Invalid home path for user {name!r}: {home!r}")
(self.rootfs_dir / safe_home).mkdir(parents=True, exist_ok=True)
uid += 1

(self.rootfs_dir / "etc" / "passwd").write_text("".join(passwd_lines))
Expand Down
2 changes: 1 addition & 1 deletion tests/ebuild/test_eos_ai.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: MIT
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 EoS Project

"""Unit tests for ebuild EoS AI module.
Expand Down
Loading