Repository navigation
ENH: record a suite outside the subject's checkout as its own benchmark source #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -88,8 +88,6 @@ def check(order: dict) -> Iterator[Refusal]: | |
| yield Refusal(f"no adapter for {suite['adapter']!r}") | ||
| if order["target"]["kind"] not in ("build", "working_tree"): | ||
| yield Refusal(f"only build and working_tree targets are supported; {order['target']['kind']!r} is deferred") | ||
| if "benchmark" in order: | ||
| yield Refusal("only benchmarks in the subject's own checkout are supported") | ||
| if "include" in suite or "exclude" in suite: | ||
| yield Refusal("only a single suites[].filter is supported, not include/exclude") | ||
| if adapter is not None: | ||
|
|
@@ -108,6 +106,18 @@ def check(order: dict) -> Iterator[Refusal]: | |
| yield from e.problems | ||
|
|
||
|
|
||
| def _benchmark_mismatches(stated: dict | None, found: dict | None): | ||
| """Refusals when the order's `benchmark` is not what the suite file turns out to be.""" | ||
| if stated is None: | ||
| return | ||
| if found is None: | ||
| yield Refusal("the order names a benchmark source, but the suite is in the subject's own checkout") | ||
| elif (stated["source"]["uri"], stated.get("revision")) != (found["uri"], found.get("revision")): | ||
| yield Refusal(f"the order names benchmark {stated['source']['uri']}" | ||
| f"{'@' + stated['revision'] if 'revision' in stated else ''}, " | ||
| f"but the suite is in {found['uri']}{'@' + found['revision'] if 'revision' in found else ''}") | ||
|
|
||
|
|
||
| def run(order_path, out_dir) -> dict: | ||
| """Execute one order; write results and the order into out_dir.""" | ||
| inspection = core.inspect(Path(order_path), "work-order") | ||
|
|
@@ -139,6 +149,14 @@ def run(order_path, out_dir) -> dict: | |
| if source is None: | ||
| # runner.md §2: a result that cannot state its revision is a defect. | ||
| refusals.append(Refusal("cannot locate the checkout the target was built from; set target.source_dir")) | ||
| benchmark = None | ||
| if source is not None and getattr(adapter, "SUITE_IS_SOURCE", False): | ||
| # The suite's own code, when it is not in the subject's checkout (runner-schema.md §3.3): | ||
| # found as it is, whatever the order says; an order that says otherwise is refused. | ||
| benchmark = snapshot.benchmark_identity(target_path / order["suites"][0]["suite"], source["path"]) | ||
| refusals += _benchmark_mismatches(order.get("benchmark"), benchmark) | ||
| elif "benchmark" in order: | ||
| refusals.append(Refusal(f"{adapter.NAME} suites are the subject's own; the order names a benchmark source")) | ||
| if refusals: | ||
| raise Refused(refusals) | ||
|
|
||
|
|
@@ -228,13 +246,15 @@ def run(order_path, out_dir) -> dict: | |
| "started_at": _timestamp(started), | ||
| "ended_at": _timestamp(started + duration), | ||
| "subject_dirty": source["dirty"], | ||
| "benchmark_dirty": source["dirty"], | ||
| "benchmark_dirty": benchmark["dirty"] if benchmark else source["dirty"], | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think right now
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| "runner": RUNNER, | ||
| **({} if condition else {"artifacts": artifacts}), | ||
| "info": info, | ||
| } | ||
| if "tree" in source: | ||
| provenance["subject_tree"] = provenance["benchmark_tree"] = source["tree"] | ||
| if benchmark: | ||
| provenance["benchmark_tree"] = benchmark["tree"] | ||
| if "labels" in order: | ||
| provenance["labels"] = order["labels"] | ||
| workload = {"name": workload_name} | ||
|
|
@@ -252,10 +272,13 @@ def run(order_path, out_dir) -> dict: | |
| **({"project": order["project"]} if "project" in order else {}), | ||
| "source": order["target"]["source"], | ||
| "revision": {"key": source["revision"]}, | ||
| "benchmark": {"source": order["target"]["source"], | ||
| # An unversioned tree is identified by its hash in provenance, not by a revision. | ||
| **({} if source["revision"].startswith("directory-") | ||
| else {"revision": {"key": source["revision"]}})}, | ||
| "benchmark": ({"source": {"uri": benchmark["uri"], "type": benchmark["type"]}, | ||
| **({"revision": {"key": benchmark["revision"]}} if "revision" in benchmark else {})} | ||
| if benchmark else | ||
| {"source": order["target"]["source"], | ||
| # An unversioned tree is identified by its hash in provenance, not by a revision. | ||
| **({} if source["revision"].startswith("directory-") | ||
| else {"revision": {"key": source["revision"]}})}), | ||
| "coordinates": { | ||
| "workload": workload, | ||
| "subject": subject, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -144,6 +144,39 @@ def source_uri(source_dir) -> str: | |
| return Path(source_dir, common).resolve().parent.as_uri() if common else Path(source_dir).resolve().as_uri() | ||
|
|
||
|
|
||
| def blob_id(path) -> str | None: | ||
| """The git blob id of a file: what `git hash-object FILE` prints.""" | ||
| try: | ||
| content = Path(path).read_bytes() | ||
| except OSError: | ||
| return None | ||
| return hashlib.sha1(b"blob %d\0" % len(content) + content).hexdigest() | ||
|
|
||
|
|
||
| def benchmark_identity(suite_file, subject_source) -> dict | None: | ||
| """Where the suite's own code lives, when that is not the subject's checkout: | ||
| None when the file is inside `subject_source`. | ||
|
|
||
| The file is the unit, not its directory (runner-schema.md §3.3). Its tree id | ||
| is its blob id. In a git checkout the source is that repository, with its | ||
| HEAD as the revision, and the file is `clean` when it equals HEAD's version | ||
| and `dirty` otherwise, including when HEAD has none. Outside git the source | ||
| is the file's directory as a `file://` URI, with no revision, and the file is | ||
| `dirty`: nothing says it was ever committed. | ||
| """ | ||
| suite = Path(suite_file).resolve() | ||
| if suite.is_relative_to(Path(subject_source).resolve()): | ||
| return None | ||
| tree = blob_id(suite) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My Claude noticed this could be too strict about line-endings (on Windows for example where the git default is CRLF). It suggests we use
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh let's punt on Windows for the moment? There are multiple places which assume POSIX, let's turn windows testing and make it run in one go separately. |
||
| top = _run(["git", "rev-parse", "--show-toplevel"], cwd=suite.parent) | ||
| head = top and _run(["git", "rev-parse", "HEAD"], cwd=top) | ||
| if head: | ||
| committed = _run(["git", "rev-parse", f"HEAD:{suite.relative_to(Path(top).resolve()).as_posix()}"], cwd=top) | ||
| return {"uri": source_uri(top), "type": "git", "revision": head, "tree": tree, | ||
| "dirty": "clean" if committed == tree else "dirty"} | ||
| return {"uri": suite.parent.as_uri(), "type": "directory", "tree": tree, "dirty": "dirty"} | ||
|
|
||
|
|
||
| _SKIPPED_DIRS = {".git", ".benchx", "__pycache__"} | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note, we would also need this for
asvafter rebase.