Skip to content
Open
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
22 changes: 18 additions & 4 deletions TASKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Status is one of: `todo`, `in-progress`, `blocked`, `review`, `done`.
| T-002 | Fix Windows Ninja test-target path parsing | backend | Maintenance | review | none |
| T-003 | `ebuild package` looks for the unsuffixed binary on Windows (`_build/app` rather than `_build/app.exe`) | backend | Maintenance | review | none |
| T-004 | `_report_footprint` (the flash/RAM report `ebuild build` prints) looks for the unsuffixed binary on Windows, and fails silently rather than logging why | backend | Maintenance | review | none |
| T-005 | Move `executable_output_path()` out of the Ninja-specific backend into a backend-neutral module (`ebuild/build/layout.py`), re-exported from `ninja_backend` for compatibility | backend | Maintenance | todo | none |
| T-005 | Move `executable_output_path()` out of the Ninja-specific backend into a backend-neutral module (`ebuild/build/layout.py`), re-exported from `ninja_backend` for compatibility | backend | Maintenance | review | none |

### Evidence (self-reported by implementer; pending independent review per `.ai/reviewer.md` — "if you implemented it, you do not approve it")

Expand Down Expand Up @@ -46,9 +46,23 @@ Status is one of: `todo`, `in-progress`, `blocked`, `review`, `done`.
process cwd's own `eos.yaml`/`board.yaml`, if any, cannot change what it
measures; confirmed to fail against the pre-fix lookup (no report
emitted) and pass against the fix.
- **Suite result** (single run, both changes present, this Windows host):
**560 passed, 6 skipped, exit code 0**. Supersedes any other count quoted
for T-003 or T-004 elsewhere in this repo or in PR #110's description.
- **T-005**: `executable_output_path()` now lives in
`ebuild/build/layout.py`; `ninja_backend` re-exports the same function for
compatibility, and CLI consumers/tests use the backend-neutral owner. The
focused suite (`tests/unit/test_package_efw.py`,
`tests/unit/test_golden_path_commands.py`, `tests/unit/test_footprint.py`,
and `tests/unit/test_ninja_backend.py`) reports **119 passed, 1 skipped**;
the compatibility test asserts both import paths are identical and the
Windows suffix path is exercised.
- **Suite result**: the earlier **560 passed, 6 skipped, exit code 0** record
predates the current branch and must not be used as its validation result.
On the current PR head, the full suite on Windows CPython 3.14 reports
**669 passed, 6 skipped, 9 failed**; all nine failures are in
`tests/unit/test_index_sync.py` because `PackageRecipe.to_dict` is missing.
The independent Linux review run selected a different platform-sensitive
set (**670 passed, 4 skipped, 10 failed**), including one build-directory
test. These failures reproduce outside T-005 and are not hidden by this
change.

## Completed

Expand Down
42 changes: 42 additions & 0 deletions ebuild/build/layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 EoS Project

"""Backend-neutral paths for generated build outputs."""

from __future__ import annotations

import sys
from pathlib import Path


def _exe_suffix() -> str:
"""Return the executable suffix used by the host platform.

Compiler drivers on Windows append ``.exe`` when ``-o`` names no
extension. Keeping that platform detail here prevents consumers from
rebuilding an output path that does not name the binary on disk.
"""
return ".exe" if sys.platform == "win32" else ""


def executable_output_path(build_dir: Path, target_name: str) -> Path:
"""Return the linked binary path for an executable or test target.

Consumers must use this helper rather than rebuilding ``build_dir /
target_name`` independently: on Windows, dropping the compiler-added
suffix makes the consumer look for a binary the build never produced.

Args:
build_dir: Directory containing the generated build files and outputs.
target_name: Name of the executable or test target.

Returns:
The target path, including ``.exe`` on Windows.

Example:
>>> from pathlib import Path
>>> executable_output_path(Path("_build"), "hello").name in (
... "hello", "hello.exe")
True
"""
return Path(build_dir) / (target_name + _exe_suffix())
37 changes: 2 additions & 35 deletions ebuild/build/ninja_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from pathlib import Path
from typing import Dict, List, Optional

from ebuild.build.layout import executable_output_path


@dataclass
class PackagePaths:
Expand All @@ -30,41 +32,6 @@ class PackagePaths:
"-fno-pie", "-fno-PIE"}


def _exe_suffix() -> str:
"""The extension the compiler driver gives an executable.

gcc on Windows appends .exe when -o names no extension, so an edge
declaring "app" produced "app.exe" on disk: ninja never saw its own
output, treated the target as dirty and relinked on every build.
"""
return ".exe" if sys.platform == "win32" else ""


def executable_output_path(build_dir: Path, target_name: str) -> Path:
"""Return the linked binary path NinjaBackend emits for *target_name*.

Args:
build_dir: Directory that contains ``build.ninja`` and the linked
outputs.
target_name: The ``name`` of an ``executable`` or ``test`` target.

Returns:
``build_dir / target_name`` on POSIX, or that path with ``.exe``
appended on Windows -- the same path the Ninja edge in
``_write_ninja`` already names via ``_exe_suffix()``. A consumer
that rebuilds this path independently instead of calling this
function can silently drop the suffix and go looking for a binary
the edge never produced.

Example:
>>> from pathlib import Path
>>> executable_output_path(Path("_build"), "hello").name in (
... "hello", "hello.exe")
True
"""
return Path(build_dir) / (target_name + _exe_suffix())


def _shared_flag() -> str:
"""The flag that makes the compiler driver emit a shared object.

Expand Down
Loading
Loading