devlyn-cli 4.2.25 → 4.2.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/codex-projection.js +129 -0
- package/bin/devlyn.js +12 -0
- package/bin/engines.js +35 -0
- package/bin/harness-activation.js +427 -0
- package/bin/harness-status.js +171 -0
- package/bin/instructions.js +13 -3
- package/bin/release.js +368 -0
- package/bin/skill-history.json +3 -0
- package/config/skills/_shared/adapters/omp.md +3 -0
- package/config/skills/_shared/runtime-principles.md +12 -2
- package/config/skills/_shared/warm-build-dirs.py +154 -83
- package/package.json +7 -1
|
@@ -16,12 +16,16 @@ Linux reflink; never a copy), removes what must not carry over, and moves the re
|
|
|
16
16
|
them: on reuse Cargo rewrites a script's own OUT_DIR in its directives, not paths elsewhere
|
|
17
17
|
(aws-lc-rs's `cargo:include` into aws-lc-sys) nor inside the text it wrote (tauri's permission
|
|
18
18
|
lists);
|
|
19
|
-
- every unit of a package whose
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
- every unit of a package whose crates read OUT_DIR while compiling, by rustc's dep-info record
|
|
20
|
+
of each env!/option_env! read in any spelling, and whose sources or generated Rust compile it
|
|
21
|
+
in other than through include!/include_str!/include_bytes!: Cargo never recompiles that path
|
|
22
|
+
(fingerprint/dep_info.rs). Telling an include from a compiled-in path is a best-effort
|
|
23
|
+
reading of the source, not a proof;
|
|
24
|
+
- incremental caches, final artifacts beside the unit directories, and outputs no fingerprint
|
|
25
|
+
claims.
|
|
26
|
+
|
|
27
|
+
Removal goes by unit hash across the layouts, so no Cargo command touches the clone. Cargo
|
|
28
|
+
rebuilds what it then finds missing, and everything that depends on it. A path that
|
|
25
29
|
survives inside compiled output (a proc-macro's install name, debug maps, a native library or
|
|
26
30
|
a crate that compiled a path in where the reading above does not see it) still names the
|
|
27
31
|
donor's files: when the donor rebuilds or removes them, the recipient can change behavior
|
|
@@ -63,12 +67,15 @@ SNIFF_BYTES = 8192 # a NUL among the first bytes marks a binary file
|
|
|
63
67
|
# a compiled-in path, and only the plain include!(concat!(env!("OUT_DIR"), ...)) forms, which
|
|
64
68
|
# embed contents, offset one each; an env!/option_env! whose argument is not a literal counts too.
|
|
65
69
|
_SPACE = r"(?:\s|//[^\n]*|/\*[\s\S]*?\*/)*"
|
|
66
|
-
#
|
|
67
|
-
|
|
70
|
+
# A literal that can decode to OUT_DIR holds only its letters, \x and \u{} escapes and string
|
|
71
|
+
# continuations; every quote may start one (a lookahead, so candidates overlap).
|
|
72
|
+
STRING_CANDIDATE = re.compile(r'"(?=((?:[OUTDIR_]|\\x[0-9a-fA-F]{2}|\\u\{[0-9a-fA-F_]+\}|\\\r?\n\s*)+)")')
|
|
68
73
|
ESCAPE = re.compile(r'\\(?:x([0-9a-fA-F]{2})|u\{([0-9a-fA-F_]+)\}|\n\s*|(.))')
|
|
69
|
-
NONLITERAL_ENV = re.compile(rf'
|
|
74
|
+
NONLITERAL_ENV = re.compile(rf'env{_SPACE}!{_SPACE}[(\[{{]{_SPACE}(?!r?#*")')
|
|
75
|
+
# Both start with a literal word, which re scans for quickly; the word's left boundary is checked
|
|
76
|
+
# on each match (a lookbehind first would make re try every position).
|
|
70
77
|
INCLUDED_OUT_DIR = re.compile(
|
|
71
|
-
r'
|
|
78
|
+
r'include(?:_str|_bytes)?\s*!\s*\(\s*concat\s*!\s*\(\s*env\s*!\s*\(\s*"OUT_DIR"\s*\)')
|
|
72
79
|
REDIRECTS = ("CARGO_TARGET_DIR", "CARGO_BUILD_TARGET_DIR", "CARGO_BUILD_BUILD_DIR")
|
|
73
80
|
RUSTC_ENV = re.compile(r"^cargo::?rustc-env=[^=]+=")
|
|
74
81
|
# Runs in a child so the time limit can stop a long clone; argv: source, target.
|
|
@@ -284,18 +291,20 @@ def publish(staging_parent, staging_name, target_parent, target_name):
|
|
|
284
291
|
|
|
285
292
|
def check_clone(staging, deadline):
|
|
286
293
|
"""No Git data, no special file, and no link that leads out of the clone."""
|
|
287
|
-
|
|
294
|
+
pending = [staging]
|
|
295
|
+
while pending:
|
|
288
296
|
deadline.left()
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
with os.scandir(pending.pop()) as entries:
|
|
298
|
+
for item in entries:
|
|
299
|
+
if item.name.casefold() == ".git":
|
|
300
|
+
raise Unsafe(f"{item.path} is Git data")
|
|
301
|
+
if item.is_symlink():
|
|
302
|
+
if not Path(os.path.realpath(item.path)).is_relative_to(staging):
|
|
303
|
+
raise Unsafe(f"{item.path} links out of the clone")
|
|
304
|
+
elif item.is_dir(follow_symlinks=False):
|
|
305
|
+
pending.append(item.path)
|
|
306
|
+
elif not item.is_file(follow_symlinks=False):
|
|
307
|
+
raise Unsafe(f"{item.path} is not a regular file or directory")
|
|
299
308
|
|
|
300
309
|
|
|
301
310
|
def mentions(data, roots):
|
|
@@ -311,7 +320,7 @@ def is_text(path):
|
|
|
311
320
|
return b"\0" not in stream.read(SNIFF_BYTES)
|
|
312
321
|
|
|
313
322
|
|
|
314
|
-
def foreign_runs(staging, layout, roots, deadline):
|
|
323
|
+
def foreign_runs(staging, layout, roots, readers, deadline):
|
|
315
324
|
"""Hashes of build-script runs whose kept results name a donor root Cargo does not rewrite."""
|
|
316
325
|
found = set()
|
|
317
326
|
for run_dir in (staging / layout / "build").iterdir():
|
|
@@ -338,7 +347,10 @@ def foreign_runs(staging, layout, roots, deadline):
|
|
|
338
347
|
deadline.left()
|
|
339
348
|
if is_text(path):
|
|
340
349
|
data = path.read_bytes()
|
|
341
|
-
|
|
350
|
+
# Generated Rust is compiled only through an include, which reads OUT_DIR.
|
|
351
|
+
foreign = mentions(data, needles) or (
|
|
352
|
+
name.endswith(".rs") and UNIT.match(run_dir.name)["name"] in readers
|
|
353
|
+
and compiles_path(data.decode(errors="replace")))
|
|
342
354
|
if foreign:
|
|
343
355
|
break
|
|
344
356
|
if foreign:
|
|
@@ -357,10 +369,20 @@ def decoded(literal):
|
|
|
357
369
|
return ESCAPE.sub(one, literal)
|
|
358
370
|
|
|
359
371
|
|
|
372
|
+
def at_word(text, start, prefix=""):
|
|
373
|
+
"""Whether text[start:] begins a word (or one led by `prefix`, as option_ leads env)."""
|
|
374
|
+
if prefix and text.endswith(prefix, 0, start):
|
|
375
|
+
start -= len(prefix)
|
|
376
|
+
return start == 0 or not (text[start - 1].isalnum() or text[start - 1] == "_")
|
|
377
|
+
|
|
378
|
+
|
|
360
379
|
def compiles_path(text):
|
|
361
380
|
"""Whether Rust source may compile its OUT_DIR in other than through an include macro."""
|
|
362
|
-
|
|
363
|
-
|
|
381
|
+
# Without OUT_DIR spelled out or a backslash, no literal can decode to it.
|
|
382
|
+
literals = (sum(decoded(match) == "OUT_DIR" for match in STRING_CANDIDATE.findall(text))
|
|
383
|
+
if "OUT_DIR" in text or "\\" in text else 0)
|
|
384
|
+
included = sum(at_word(text, m.start()) for m in INCLUDED_OUT_DIR.finditer(text)) if "OUT_DIR" in text else 0
|
|
385
|
+
return literals > included or any(at_word(text, m.start(), "option_") for m in NONLITERAL_ENV.finditer(text))
|
|
364
386
|
|
|
365
387
|
|
|
366
388
|
def compiles_out_dir(package, deadline):
|
|
@@ -415,36 +437,61 @@ def remove_unclassified(staging, layout, deadline):
|
|
|
415
437
|
remove(item, deadline)
|
|
416
438
|
|
|
417
439
|
|
|
440
|
+
def crates_reading_out_dir(staging, found, crates, deadline):
|
|
441
|
+
"""The crates among these that may have read OUT_DIR while compiling, by rustc's dep-info.
|
|
442
|
+
|
|
443
|
+
rustc writes `# env-dep:OUT_DIR=...` into a unit's deps/<crate>-<hash>.d for every
|
|
444
|
+
env!/option_env! read of it, whatever the spelling or macro that made it. A crate counts as
|
|
445
|
+
not reading it only when every kept output of it has that file and none says so. Any
|
|
446
|
+
hashed output counts, with or without the `lib` prefix: rlibs, rmeta, dylibs, Windows DLLs
|
|
447
|
+
and static libraries, test executables.
|
|
448
|
+
"""
|
|
449
|
+
reading, recorded, outputs = set(), {}, {}
|
|
450
|
+
for layout in found:
|
|
451
|
+
for item in (staging / layout / "deps").iterdir():
|
|
452
|
+
deadline.left()
|
|
453
|
+
if not (match := UNIT.match(item.name)):
|
|
454
|
+
continue
|
|
455
|
+
if item.name.endswith(".d"):
|
|
456
|
+
if match["name"] in crates:
|
|
457
|
+
recorded.setdefault(match["name"], set()).add(match["hash"])
|
|
458
|
+
if b"\n# env-dep:OUT_DIR=" in b"\n" + item.read_bytes():
|
|
459
|
+
reading.add(match["name"])
|
|
460
|
+
continue
|
|
461
|
+
stem = match["name"]
|
|
462
|
+
for crate in {stem, stem.removeprefix("lib")} & crates:
|
|
463
|
+
outputs.setdefault(crate, set()).add(match["hash"])
|
|
464
|
+
return reading | {crate for crate, hashes in outputs.items() if hashes - recorded.get(crate, set())}
|
|
465
|
+
|
|
466
|
+
|
|
418
467
|
def sanitize(work, staging, found, roots, deadline):
|
|
419
468
|
for layout in found:
|
|
420
469
|
remove_unclassified(staging, layout, deadline)
|
|
421
|
-
|
|
422
|
-
resolved = metadata(work, deadline, env=env, deps=True)
|
|
423
|
-
if not (Path(resolved["target_directory"]).resolve() == Path(resolved["build_directory"]).resolve() == staging.resolve()):
|
|
424
|
-
raise Unsafe("Cargo does not resolve the staging directory as both target and build directory")
|
|
470
|
+
resolved = metadata(work, deadline, env=without_redirects(), deps=True)
|
|
425
471
|
names = {package["name"] for package in resolved["packages"]}
|
|
426
|
-
path_packages =
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
472
|
+
path_packages = {package["name"] for package in resolved["packages"] if package["source"] is None}
|
|
473
|
+
# Every version of a package counts on its own; a name is removed if any version must be.
|
|
474
|
+
scripted = [package for package in resolved["packages"]
|
|
475
|
+
if package["source"] is not None and any("custom-build" in t["kind"] for t in package["targets"])]
|
|
476
|
+
crates = [{t["name"].replace("-", "_") for t in package["targets"] if "custom-build" not in t["kind"]}
|
|
477
|
+
for package in scripted]
|
|
478
|
+
reading = crates_reading_out_dir(staging, found, set().union(*crates), deadline)
|
|
479
|
+
reader_packages = [package for package, own in zip(scripted, crates) if own & reading]
|
|
480
|
+
readers = {package["name"] for package in reader_packages}
|
|
481
|
+
out_dir_packages = {package["name"] for package in reader_packages if compiles_out_dir(package, deadline)}
|
|
430
482
|
removed = set()
|
|
431
483
|
for layout in found:
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
run(base, cwd=work, deadline=deadline, env=env) # Without --target only the host layout's outputs go.
|
|
444
|
-
triples = [arg for layout in found[1:] for arg in ("--target", layout.parts[0])]
|
|
445
|
-
if triples:
|
|
446
|
-
run([*base, *triples], cwd=work, deadline=deadline, env=env)
|
|
447
|
-
return sorted(removed | set(clean))
|
|
484
|
+
units = {m["hash"]: m["name"] for item in (staging / layout / ".fingerprint").iterdir()
|
|
485
|
+
if (m := UNIT.match(item.name))}
|
|
486
|
+
drop = {digest for digest, name in units.items()
|
|
487
|
+
if name not in names or name in path_packages or name in out_dir_packages}
|
|
488
|
+
drop |= foreign_runs(staging, layout, roots, readers, deadline)
|
|
489
|
+
removed |= {units[digest] for digest in drop if digest in units}
|
|
490
|
+
# Outputs without a fingerprint belong to no unit Cargo would reuse.
|
|
491
|
+
drop |= {m["hash"] for sub in ("build", "deps", "examples") if (staging / layout / sub).is_dir()
|
|
492
|
+
for item in (staging / layout / sub).iterdir() if (m := UNIT.match(item.name)) and m["hash"] not in units}
|
|
493
|
+
remove_units(staging, layout, drop, deadline)
|
|
494
|
+
return sorted(removed)
|
|
448
495
|
|
|
449
496
|
|
|
450
497
|
def warm_cargo(work, anchor, staging_root, deadline):
|
|
@@ -575,34 +622,24 @@ def self_test():
|
|
|
575
622
|
return 0 if result.wasSuccessful() else 1
|
|
576
623
|
|
|
577
624
|
|
|
578
|
-
# Fixtures use real Git and a fake `cargo` on PATH that answers `metadata` from a JSON file
|
|
579
|
-
#
|
|
580
|
-
# copies instead, in tests only, so every rule still runs there.
|
|
625
|
+
# Fixtures use real Git and a fake `cargo` on PATH that answers `metadata` from a JSON file;
|
|
626
|
+
# a filesystem without cloning copies instead, in tests only, so every rule still runs there.
|
|
581
627
|
FAKE_CARGO = r"""#!/usr/bin/env python3
|
|
582
|
-
import json, os, pathlib,
|
|
628
|
+
import json, os, pathlib, sys, time
|
|
583
629
|
argv = sys.argv[1:]
|
|
584
630
|
fixture = json.loads(pathlib.Path(os.environ["FAKE_CARGO"]).read_text())
|
|
585
631
|
with open(os.environ["FAKE_CARGO_LOG"], "a") as log:
|
|
586
632
|
log.write(json.dumps(argv) + "\n")
|
|
587
633
|
time.sleep(fixture.get("sleep", 0))
|
|
588
634
|
target = pathlib.Path(os.environ.get("CARGO_TARGET_DIR") or os.getcwd() + "/" + fixture.get("target", "target"))
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
names = [argv[i + 1] for i, a in enumerate(argv) if a == "-p"]
|
|
598
|
-
triples = [argv[i + 1] for i, a in enumerate(argv) if a == "--target"]
|
|
599
|
-
for layout in [directory / t / "debug" for t in triples] or [directory / "debug"]:
|
|
600
|
-
for name in names:
|
|
601
|
-
for sub in (".fingerprint", "build", "deps"):
|
|
602
|
-
for item in (layout / sub).glob("*"):
|
|
603
|
-
stem = item.name.split(".")[0].removeprefix("lib")
|
|
604
|
-
if stem.rsplit("-", 1)[0].replace("_", "-") == name:
|
|
605
|
-
shutil.rmtree(item) if item.is_dir() else item.unlink()
|
|
635
|
+
assert argv[0] == "metadata", argv # warming never runs another cargo command
|
|
636
|
+
found = {"target_directory": str(target), "packages": fixture["packages"] if "--no-deps" not in argv else [],
|
|
637
|
+
"metadata": fixture.get("workspace_metadata", {"devlyn": {"warm-build-dirs": True}})}
|
|
638
|
+
if not fixture.get("old_cargo"):
|
|
639
|
+
found["build_directory"] = fixture.get("build_directory", str(target))
|
|
640
|
+
print(json.dumps(found))
|
|
641
|
+
# The full resolve runs while warming, after the clone: the moment to race the destination.
|
|
642
|
+
if "--no-deps" not in argv:
|
|
606
643
|
if fixture.get("swap") and not os.path.islink("build"):
|
|
607
644
|
os.rename("build", "build-moved")
|
|
608
645
|
os.symlink(fixture["swap"], "build")
|
|
@@ -669,20 +706,29 @@ class WarmTests(unittest.TestCase):
|
|
|
669
706
|
|
|
670
707
|
def write_fixture(self, **extra):
|
|
671
708
|
packages = [
|
|
672
|
-
{"name": "app", "source": None, "manifest_path": str(self.work / "Cargo.toml"), "targets": [{"kind": ["lib"]}]},
|
|
673
|
-
{"name": "serde", "source": "registry", "manifest_path": str(self.root / "registry/serde/Cargo.toml"), "targets": [{"kind": ["lib"]}]},
|
|
674
|
-
{"name": "ring", "source": "registry", "manifest_path": str(self.root / "registry/ring/Cargo.toml"), "targets": [{"kind": ["lib"]}, {"kind": ["custom-build"]}]},
|
|
675
|
-
{"name": "tauri", "source": "registry", "manifest_path": str(self.root / "registry/tauri/Cargo.toml"), "targets": [{"kind": ["lib"]}, {"kind": ["custom-build"]}]},
|
|
676
|
-
{"name": "aws-lc-rs", "source": "registry", "manifest_path": str(self.root / "registry/aws-lc-rs/Cargo.toml"), "targets": [{"kind": ["lib"]}, {"kind": ["custom-build"]}]},
|
|
677
|
-
{"name": "baked", "source": "registry", "manifest_path": str(self.root / "registry/baked/Cargo.toml"), "targets": [{"kind": ["lib"]}, {"kind": ["custom-build"]}]},
|
|
678
|
-
{"name": "exporter", "source": "registry", "manifest_path": str(self.root / "registry/exporter/Cargo.toml"), "targets": [{"kind": ["lib"]}, {"kind": ["custom-build"]}]},
|
|
679
|
-
{"name": "generator", "source": "registry", "manifest_path": str(self.root / "registry/generator/Cargo.toml"), "targets": [{"kind": ["lib"]}, {"kind": ["custom-build"]}]},
|
|
709
|
+
{"name": "app", "source": None, "manifest_path": str(self.work / "Cargo.toml"), "targets": [{"kind": ["lib"], "name": "LIB"}]},
|
|
710
|
+
{"name": "serde", "source": "registry", "manifest_path": str(self.root / "registry/serde/Cargo.toml"), "targets": [{"kind": ["lib"], "name": "LIB"}]},
|
|
711
|
+
{"name": "ring", "source": "registry", "manifest_path": str(self.root / "registry/ring/Cargo.toml"), "targets": [{"kind": ["lib"], "name": "LIB"}, {"kind": ["custom-build"], "name": "build-script-build"}]},
|
|
712
|
+
{"name": "tauri", "source": "registry", "manifest_path": str(self.root / "registry/tauri/Cargo.toml"), "targets": [{"kind": ["lib"], "name": "LIB"}, {"kind": ["custom-build"], "name": "build-script-build"}]},
|
|
713
|
+
{"name": "aws-lc-rs", "source": "registry", "manifest_path": str(self.root / "registry/aws-lc-rs/Cargo.toml"), "targets": [{"kind": ["lib"], "name": "LIB"}, {"kind": ["custom-build"], "name": "build-script-build"}]},
|
|
714
|
+
{"name": "baked", "source": "registry", "manifest_path": str(self.root / "registry/baked/Cargo.toml"), "targets": [{"kind": ["lib"], "name": "LIB"}, {"kind": ["custom-build"], "name": "build-script-build"}]},
|
|
715
|
+
{"name": "exporter", "source": "registry", "manifest_path": str(self.root / "registry/exporter/Cargo.toml"), "targets": [{"kind": ["lib"], "name": "LIB"}, {"kind": ["custom-build"], "name": "build-script-build"}]},
|
|
716
|
+
{"name": "generator", "source": "registry", "manifest_path": str(self.root / "registry/generator/Cargo.toml"), "targets": [{"kind": ["lib"], "name": "LIB"}, {"kind": ["custom-build"], "name": "build-script-build"}]},
|
|
717
|
+
{"name": "windowsdll", "source": "registry", "manifest_path": str(self.root / "registry/windowsdll/Cargo.toml"), "targets": [{"kind": ["dylib"], "name": "LIB"}, {"kind": ["custom-build"], "name": "build-script-build"}]},
|
|
718
|
+
# rustc left no dep-info for its library, so nothing proves it never read OUT_DIR.
|
|
719
|
+
{"name": "undocumented", "source": "registry", "manifest_path": str(self.root / "registry/undocumented/Cargo.toml"), "targets": [{"kind": ["lib"], "name": "LIB"}, {"kind": ["custom-build"], "name": "build-script-build"}]},
|
|
720
|
+
# Two versions of one package: only 1.0 compiles OUT_DIR in.
|
|
721
|
+
{"name": "dual", "source": "registry", "manifest_path": str(self.root / "registry/dual-1/Cargo.toml"), "targets": [{"kind": ["lib"], "name": "LIB"}, {"kind": ["custom-build"], "name": "build-script-build"}]},
|
|
722
|
+
{"name": "dual", "source": "registry", "manifest_path": str(self.root / "registry/dual-2/Cargo.toml"), "targets": [{"kind": ["lib"], "name": "LIB"}, {"kind": ["custom-build"], "name": "build-script-build"}]},
|
|
680
723
|
]
|
|
724
|
+
for package in packages:
|
|
725
|
+
package["targets"] = [{**t, "name": package["name"] if t["name"] == "LIB" else t["name"]} for t in package["targets"]]
|
|
681
726
|
for package in packages[1:]:
|
|
682
727
|
source = Path(package["manifest_path"]).parent / "src"
|
|
683
728
|
source.mkdir(parents=True, exist_ok=True)
|
|
684
|
-
|
|
685
|
-
|
|
729
|
+
compiled = Path(package["manifest_path"]).parent.name in {"baked", "undocumented", "dual-1", "windowsdll"}
|
|
730
|
+
(source / "lib.rs").write_text('pub const DIR: &str = env!(/* why */ "OUT_DIR");\n' if compiled
|
|
731
|
+
else 'include!(concat!(\n env!("OUT_DIR"), "/gen.rs"));\n')
|
|
686
732
|
self.fixture.write_text(json.dumps({"packages": packages, **extra}))
|
|
687
733
|
|
|
688
734
|
def unit(self, layout, sub, name, digest, files=None):
|
|
@@ -706,6 +752,24 @@ class WarmTests(unittest.TestCase):
|
|
|
706
752
|
self.unit(layout, ".fingerprint", name, digest)
|
|
707
753
|
(deps / f"lib{name}-{digest}.rlib").write_bytes(b"rlib")
|
|
708
754
|
(self.donor / layout / ".cargo-lock").write_bytes(b"")
|
|
755
|
+
# rustc's dep-info: which crates read OUT_DIR while compiling.
|
|
756
|
+
for crate, digest, reads in (("serde", "1" * 16, False), ("baked", "8" * 16, True), ("generator", "b" * 16, True),
|
|
757
|
+
("ring", "c" * 16, False), ("dual", "1a" * 8, True), ("dual", "2b" * 8, True)):
|
|
758
|
+
record = f"# env-dep:OUT_DIR={self.donor}/{layout}/build/{crate}-x/out\n" if reads else ""
|
|
759
|
+
(deps / f"{crate}-{digest}.d").write_text(f"/src/{crate}/lib.rs:\n\n# env-dep:CARGO_PKG_NAME={crate}\n{record}")
|
|
760
|
+
self.unit(layout, ".fingerprint", crate, digest)
|
|
761
|
+
(deps / "libstray-dddddddddddddddd.rlib").write_bytes(b"rlib")
|
|
762
|
+
self.unit(layout, ".fingerprint", "undocumented", "e" * 16)
|
|
763
|
+
(deps / f"libundocumented-{'e' * 16}.rlib").write_bytes(b"rlib")
|
|
764
|
+
# A Windows-style DLL (no `lib` prefix) without dep-info, of a crate that compiles OUT_DIR in.
|
|
765
|
+
self.unit(layout, ".fingerprint", "windowsdll", "9a" * 8)
|
|
766
|
+
(deps / f"windowsdll-{'9a' * 8}.dll").write_bytes(b"dll")
|
|
767
|
+
for digest in ("1a" * 8, "2b" * 8):
|
|
768
|
+
(deps / f"libdual-{digest}.rlib").write_bytes(b"rlib")
|
|
769
|
+
# A run that points into the donor and has no fingerprint left.
|
|
770
|
+
self.unit(layout, "build", "orphanrun", "f" * 16, {
|
|
771
|
+
"root-output": f"{self.donor}/{layout}/build/orphanrun-{'f' * 16}/out".encode(),
|
|
772
|
+
"output": f"cargo:include={self.donor}/{layout}/build/aws-lc-sys-{'6' * 16}/out\n".encode()})
|
|
709
773
|
(self.donor / layout / "incremental" / "app-x").mkdir(parents=True)
|
|
710
774
|
# Final artifacts Cargo puts beside the unit directories, here of the unused `gone`.
|
|
711
775
|
(self.donor / layout / "old-tool").write_bytes(b"bin")
|
|
@@ -743,11 +807,18 @@ class WarmTests(unittest.TestCase):
|
|
|
743
807
|
target = self.work / "target"
|
|
744
808
|
for layout in ("debug", "x86_64-pc-windows-msvc/debug"):
|
|
745
809
|
kept = {p.name for p in (target / layout / ".fingerprint").iterdir()}
|
|
746
|
-
|
|
810
|
+
# generator keeps its library unit: Cargo rebuilds it after rerunning the removed run.
|
|
811
|
+
self.assertEqual({f"serde-{'1' * 16}", f"ring-{'4' * 16}", f"ring-{'c' * 16}", f"generator-{'b' * 16}"}, kept, layout)
|
|
812
|
+
self.assertFalse((target / layout / "deps" / "libstray-dddddddddddddddd.rlib").exists())
|
|
747
813
|
self.assertEqual({".fingerprint", "build", "deps", "examples", ".cargo-lock"}, {p.name for p in (target / layout).iterdir()})
|
|
748
814
|
self.assertEqual([], list((target / layout / "examples").iterdir()))
|
|
749
815
|
self.assertTrue((target / layout / "build" / f"ring-{'4' * 16}" / "out" / "libring.a").exists())
|
|
750
|
-
self.assertEqual(["app", "aws-lc-rs", "baked", "exporter", "generator", "gone", "tauri"
|
|
816
|
+
self.assertEqual(["app", "aws-lc-rs", "baked", "dual", "exporter", "generator", "gone", "tauri", "undocumented",
|
|
817
|
+
"windowsdll"],
|
|
818
|
+
result["removed"])
|
|
819
|
+
for layout in ("debug", "x86_64-pc-windows-msvc/debug"):
|
|
820
|
+
self.assertFalse((target / layout / "build" / f"orphanrun-{'f' * 16}").exists())
|
|
821
|
+
self.assertFalse(any((target / layout / "deps").glob("libdual-*")))
|
|
751
822
|
self.assertEqual(str(self.donor), result["donor"])
|
|
752
823
|
self.assertEqual(before, self.snapshot(self.donor))
|
|
753
824
|
self.assert_nothing_left()
|
|
@@ -876,7 +947,7 @@ class WarmTests(unittest.TestCase):
|
|
|
876
947
|
'option_env!("OUT_DIR")', 'myinclude!(concat!(env!("OUT_DIR"), "/a.rs"))', 'env![ "OUT_DIR" ]',
|
|
877
948
|
'env/**/!("OUT_DIR")', 'env!(/* why */ "OUT_DIR")', 'env!("OUT\\x5fDIR")', 'env!("\\u{4f}UT_DIR")',
|
|
878
949
|
'use std::env as e; e!("OUT_DIR")', 'env!(concat!("OUT", "_DIR"))', 'env!(// note\n"OUT_DIR")',
|
|
879
|
-
'env!("\\u{4_f}UT_DIR")', 'env!("OUT_\\\n DIR")', 'let a = \'"\'; env!("OUT_DIR")',
|
|
950
|
+
'env!("\\u{4_f}UT_DIR")', 'env!("\\u{0_0_0_0_4_f}UT_DIR")', 'env!("OUT_\\\n DIR")', 'let a = \'"\'; env!("OUT_DIR")',
|
|
880
951
|
'include!(concat!(env!(/* c */ "OUT_DIR"), "/a.rs"))']
|
|
881
952
|
for text in compiled:
|
|
882
953
|
self.assertTrue(compiles_path(text), text)
|
package/package.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devlyn-cli",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.26",
|
|
4
4
|
"description": "Loop & harness engineering for AI coding agents (Claude Code, Codex, and any AGENTS.md-driven agent) — core engineering principles for CLAUDE.md/AGENTS.md and a loop designer that splits one intent into tasks agents drain hands-free",
|
|
5
5
|
"homepage": "https://github.com/fysoul17/devlyn-cli#readme",
|
|
6
6
|
"bin": {
|
|
7
7
|
"devlyn": "bin/devlyn.js"
|
|
8
8
|
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"prepack": "node -e \"require('./bin/release').buildRelease()\""
|
|
11
|
+
},
|
|
9
12
|
"files": [
|
|
10
13
|
"bin",
|
|
11
14
|
"config",
|
|
15
|
+
"harness",
|
|
16
|
+
"native",
|
|
17
|
+
"release.json",
|
|
12
18
|
"!config/skills/preflight-workspace",
|
|
13
19
|
"!config/skills/preflight-workspace/**",
|
|
14
20
|
"!config/skills/devlyn:ideate-workspace",
|