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
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,3 @@ task_plan.md
.ccache

.trellis

# Stray runtime artifact from cwd-polluted test runs
/checklist.json
48 changes: 13 additions & 35 deletions chipcompiler/cli/project/run_existing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
wiring belongs next to the ledger it owns.
"""

import sys
from pathlib import Path

from chipcompiler.cli.core.output import disclosure_cmd
from chipcompiler.cli.core.types import CommandResult
from chipcompiler.cli.project.run_prepare import _write_back_status
from chipcompiler.data import is_finished_step_state


def run_existing_workspace(
Expand Down Expand Up @@ -187,49 +185,29 @@ def mismatch_error(reason: str) -> CommandResult:
]
)

from chipcompiler.cli.rendering.progress import preserve_cli_stdio
from chipcompiler.engine import EngineFlow
from chipcompiler.engine.rerun import bounded_resume_names, run_resume, selected_step_names

try:
engine_flow = EngineFlow(workspace=workspace)
flow_ok = True
if result.outcome != "no_op":
# Re-read the ledger: reconcile may have appended suffix steps
# after load_workspace populated the in-memory copy.
from chipcompiler.utility import json_read
engine_flow.load()

flow_data = json_read(workspace.flow.path or Path(run_dir) / "home" / "flow.json")
target_names = set(result.target)
executable = {
step["name"]
for step in flow_data.get("steps", [])
if isinstance(step, dict)
and isinstance(step.get("name"), str)
and not is_finished_step_state(step.get("state"))
and step["name"] in target_names
}
engine_flow.create_step_workspaces(executable_steps=executable)
# executable_steps only gates dependency verification; the
# actual runner iterates every workspace step. Bind execution
# to the reconciled target so a wider persisted ledger (e.g.
# RCX/sta beyond the requested end) never runs on resume.
engine_flow.workspace_steps = [
step
for step in getattr(engine_flow, "workspace_steps", None) or []
if step.name in target_names
]

from chipcompiler.cli.rendering.progress import (
run_flow_with_progress,
should_enable_run_progress,
)

if should_enable_run_progress(ctx, sys.stderr):
flow_ok = run_flow_with_progress(engine_flow, ctx, project, sys.stderr)
through = result.target[-1] if result.target else None
if through is not None:
selected = bounded_resume_names(engine_flow, through)
else:
# The persisted ledger may be wider than the reconciled
# target by design (workspace_steps is bound above), so
# the full-ledger completeness check does not apply.
flow_ok = engine_flow.run_steps(require_full_ledger=False)
selected = selected_step_names(engine_flow)
if selected:
engine_flow.create_step_workspaces(executable_steps=set(selected))

with preserve_cli_stdio():
run_result = run_resume(engine_flow, through=through)
flow_ok = run_result.ok
except Exception as exc:
if workspace_registered:
_write_back_status(project_dir, run_name, "failed", warnings)
Expand Down
5 changes: 4 additions & 1 deletion test/cli/commands/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ def has_init(self):
def add_step(self, step, tool, state):
self.added_steps.append((step, tool, state))

def create_step_workspaces(self):
def load(self):
return True

def create_step_workspaces(self, **_kwargs):
self.create_called = True

def run_steps(self, **_kwargs):
Expand Down
70 changes: 68 additions & 2 deletions test/cli/commands/test_flow_continuation.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,68 @@ def create_step_workspaces(self, *, executable_steps=None):
assert records[0]["status"] == "success"
assert records[0]["no_op"] == "True"

def test_stale_suffix_reexecuted_via_run_resume(
self,
tmp_path,
capsys,
create_cli_project,
minimal_ics55_pdk_factory,
monkeypatch,
plain_records,
):
"""Regression: place=Incomplete + CTS=Success must call run_resume
(not run_steps), so the stale suffix is re-executed."""
pdk_root = minimal_ics55_pdk_factory(tmp_path / "ics55")
project_dir = create_cli_project(pdk_root=pdk_root)
monkeypatch.setattr(
"chipcompiler.cli.project.config._validate_pdk_contents",
lambda name, root, overrides=None: None,
)
run_dir = os.path.join(project_dir, "default")
_write_existing_workspace(
run_dir,
RTL2GDS_NAMES,
states=(
["Success", "Success", "Success", "Incomplete", "Success"]
+ ["Unstart"] * (len(RTL2GDS_NAMES) - 5)
),
pdk_root=pdk_root,
)

from chipcompiler.engine.rerun import StepRunResult

resume_calls = []

def spy_run_resume(flow, *, through=None):
resume_calls.append(through)
return StepRunResult(ok=True, executed=())

class Flow:
def __init__(self, workspace):
self.workspace = workspace

def create_step_workspaces(self, *, executable_steps=None):
return None

def load(self):
from chipcompiler.utility import json_read

path = self.workspace.flow.path
if path:
self.workspace.flow.data = json_read(path)
return bool(self.workspace.flow.data.get("steps", []))

monkeypatch.setattr("chipcompiler.engine.EngineFlow", Flow)
monkeypatch.setattr("chipcompiler.engine.rerun.run_resume", spy_run_resume)

rc = cli_main.run(["run", "--project", project_dir, "--plain"])

assert rc == 0
assert len(resume_calls) == 1
assert resume_calls[0] == RTL2GDS_NAMES[-1]
records = _records(capsys, plain_records)
assert records[0]["status"] == "success"

def test_set_rejected_on_existing_run(
self,
tmp_path,
Expand Down Expand Up @@ -411,10 +473,14 @@ def __init__(self, workspace):
def create_step_workspaces(self, *, executable_steps=None):
return None

def run_steps(self, **_kwargs):
raise RuntimeError("engine exploded")
def load(self):
return True

def fake_run_resume(*_args, **_kwargs):
raise RuntimeError("engine exploded")

monkeypatch.setattr("chipcompiler.engine.EngineFlow", Flow)
monkeypatch.setattr("chipcompiler.engine.rerun.run_resume", fake_run_resume)

rc = cli_main.run(["run", "--project", project_dir, "--plain"])

Expand Down
Loading