docs(mosaic): preserve SceneFX ABI and packaging feasibility evidence
This commit is contained in:
parent
7c22b4020e
commit
bb3a112550
12 changed files with 1040 additions and 0 deletions
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
### STEP20C experimental evidence
|
||||
|
||||
- Preserved SceneFX/wlroots ABI/package audit: current stock build and exact-one-file staging PASS; experimental shared clip functions absent from stock despite common SONAME. Private static build/loader passes, but wlroots interposition, notices, security and version-maintenance gates remain. PARTIALLY_FEASIBLE; architectural decision required. No integration or system change; the historical ABI evidence is published separately from any later distribution-contract investigation.
|
||||
|
||||
- Preserved optimized-edge gate: four completed controls reproduce the same boundary differences in pristine SceneFX, private splith and Mosaic without OPT traces. Same-binary Mosaic trace-off/on and pristine/private standard match full RGB at six states; central freshness, H2 stability and sampled clipping hold. PARTIALLY_FEASIBLE, architecture UNDECIDED: reduced-domain FBO history has source/model support but no native causal observation. Separate ABI/package audit justified; optional bounded GPU study remains unexecuted. No production change; historical graphical campaign closed.
|
||||
|
||||
- Preserved optimized blur cache probe: private splith/Mosaic used a real background source and output-scoped GPU cache, naturally reconstructed A/B/A and restored exact central filtered pixels with stable whole H2 RGB. Boundary equivalence still differs by up to six RGB levels in both private modes. Pristine C0 failed before captures; its duplicate initial image was charged after observation, with no retry or numerical budget overrun. PARTIALLY_FEASIBLE, architecture UNDECIDED; neutrality and whole-cache correctness unqualified. No production change; the historical campaign is closed.
|
||||
|
|
|
|||
|
|
@ -342,3 +342,14 @@ extents cover stabilized differences only. No clipping regression or required
|
|||
invariant violation is established. A separate SceneFX ABI/package audit is
|
||||
now justified, retaining this shared rendering limitation; optional selective
|
||||
GPU observation requires a new scoped ticket. This evidence is preserved without a production change. STEP20C stays NEXT, production multi-output NOT YET SUPPORTED.
|
||||
|
||||
The preserved [SceneFX ABI/package audit](design/STEP20C_SCENEFX_ABI_PACKAGE_FEASIBILITY.md)
|
||||
is **PARTIALLY_FEASIBLE**, architecture
|
||||
**SCENEFX_ABI_PACKAGE_REQUIRES_ARCHITECTURAL_DECISION**. The unchanged current
|
||||
package builds with stock SceneFX and stages exactly /usr/bin/swayfx-mosaic.
|
||||
The shared experimental binary needs two clip exports absent from stock
|
||||
despite the same SONAME. Static embedding builds and resolves without a
|
||||
SceneFX shared dependency, but shared wlroots interposition, updates, security
|
||||
maintenance and license notices remain gates. A distinct private shared library
|
||||
would require a new file-ownership decision; upstream API acceptance is unknown.
|
||||
No integration or packaging contract is approved. The historical audit is frozen as evidence; STEP20C stays NEXT, production multi-output NOT YET SUPPORTED.
|
||||
|
|
|
|||
|
|
@ -283,3 +283,14 @@ extents cover stabilized differences only. No clipping regression or required
|
|||
invariant violation is established. A separate SceneFX ABI/package audit is
|
||||
now justified, retaining this shared rendering limitation; optional selective
|
||||
GPU observation requires a new scoped ticket. This evidence is preserved without a production change. STEP20C stays NEXT, production multi-output NOT YET SUPPORTED.
|
||||
|
||||
The preserved [SceneFX ABI/package audit](design/STEP20C_SCENEFX_ABI_PACKAGE_FEASIBILITY.md)
|
||||
is **PARTIALLY_FEASIBLE**, architecture
|
||||
**SCENEFX_ABI_PACKAGE_REQUIRES_ARCHITECTURAL_DECISION**. The unchanged current
|
||||
package builds with stock SceneFX and stages exactly /usr/bin/swayfx-mosaic.
|
||||
The shared experimental binary needs two clip exports absent from stock
|
||||
despite the same SONAME. Static embedding builds and resolves without a
|
||||
SceneFX shared dependency, but shared wlroots interposition, updates, security
|
||||
maintenance and license notices remain gates. A distinct private shared library
|
||||
would require a new file-ownership decision; upstream API acceptance is unknown.
|
||||
No integration or packaging contract is approved. The historical audit is frozen as evidence; STEP20C stays NEXT, production multi-output NOT YET SUPPORTED.
|
||||
|
|
|
|||
|
|
@ -41,3 +41,14 @@ The repository contains full source for development. The exporter constructs the
|
|||
6. If Mosaic fails, use the normal SwayFX session. Do not overwrite `/usr/bin/sway` to recover.
|
||||
|
||||
See [upstream maintenance](UPSTREAM.md) and [development](DEVELOPMENT.md) for the operational procedure.
|
||||
|
||||
## Preserved experimental ABI/package audit
|
||||
|
||||
The [STEP20C audit](design/STEP20C_SCENEFX_ABI_PACKAGE_FEASIBILITY.md) separately
|
||||
validates the unchanged current package's stock build and exact-one-file stage.
|
||||
The experimental shared binary cannot resolve its two private clip functions
|
||||
with stock SceneFX. A private static build removes SceneFX DT_NEEDED, but does
|
||||
not establish shared-wlroots update safety or a one-file notice policy.
|
||||
A distinct private dynamic library would add an owned file and require a new
|
||||
explicit packaging decision. No option is adopted; the contract above remains
|
||||
authoritative and unchanged. The historical evidence is frozen, PARTIALLY_FEASIBLE.
|
||||
|
|
|
|||
|
|
@ -195,3 +195,17 @@ GPU observation requires a new scoped ticket. This evidence is preserved without
|
|||
|
||||
The [preserved edge source fixture](../tooling/probes/step20c-optimized-blur-edge/README.md)
|
||||
records pinned reconstruction, offline checks and the exhausted four-slot ledger.
|
||||
|
||||
The preserved [SceneFX ABI/package audit](design/STEP20C_SCENEFX_ABI_PACKAGE_FEASIBILITY.md)
|
||||
is **PARTIALLY_FEASIBLE**, architecture
|
||||
**SCENEFX_ABI_PACKAGE_REQUIRES_ARCHITECTURAL_DECISION**. The unchanged current
|
||||
package builds with stock SceneFX and stages exactly /usr/bin/swayfx-mosaic.
|
||||
The shared experimental binary needs two clip exports absent from stock
|
||||
despite the same SONAME. Static embedding builds and resolves without a
|
||||
SceneFX shared dependency, but shared wlroots interposition, updates, security
|
||||
maintenance and license notices remain gates. A distinct private shared library
|
||||
would require a new file-ownership decision; upstream API acceptance is unknown.
|
||||
No integration or packaging contract is approved. The historical audit is frozen as evidence; STEP20C stays NEXT, production multi-output NOT YET SUPPORTED.
|
||||
|
||||
The [ABI/package source fixture](../tooling/probes/step20c-scenefx-abi-package/README.md)
|
||||
records build-only reconstruction and non-graphical diagnostic commands.
|
||||
|
|
|
|||
|
|
@ -287,3 +287,14 @@ extents cover stabilized differences only. No clipping regression or required
|
|||
invariant violation is established. A separate SceneFX ABI/package audit is
|
||||
now justified, retaining this shared rendering limitation; optional selective
|
||||
GPU observation requires a new scoped ticket. This evidence is preserved without a production change. STEP20C stays NEXT, production multi-output NOT YET SUPPORTED.
|
||||
|
||||
The preserved [SceneFX ABI/package audit](design/STEP20C_SCENEFX_ABI_PACKAGE_FEASIBILITY.md)
|
||||
is **PARTIALLY_FEASIBLE**, architecture
|
||||
**SCENEFX_ABI_PACKAGE_REQUIRES_ARCHITECTURAL_DECISION**. The unchanged current
|
||||
package builds with stock SceneFX and stages exactly /usr/bin/swayfx-mosaic.
|
||||
The shared experimental binary needs two clip exports absent from stock
|
||||
despite the same SONAME. Static embedding builds and resolves without a
|
||||
SceneFX shared dependency, but shared wlroots interposition, updates, security
|
||||
maintenance and license notices remain gates. A distinct private shared library
|
||||
would require a new file-ownership decision; upstream API acceptance is unknown.
|
||||
No integration or packaging contract is approved. The historical audit is frozen as evidence; STEP20C stays NEXT, production multi-output NOT YET SUPPORTED.
|
||||
|
|
|
|||
441
docs/design/STEP20C_SCENEFX_ABI_PACKAGE_FEASIBILITY.md
Normal file
441
docs/design/STEP20C_SCENEFX_ABI_PACKAGE_FEASIBILITY.md
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
# STEP20C SceneFX / wlroots ABI and package feasibility
|
||||
|
||||
**EXPERIMENTAL — PRESERVED HISTORICAL EVIDENCE.**
|
||||
Protocol recorded before builds. A freeze gate passed at
|
||||
`7c22b4020edc2d9ab6779d789159937523aae75a` on local, Forgejo and GitHub,
|
||||
with a clean worktree. STEP20A DONE, STEP20B EXPERIMENTAL, STEP20C NEXT,
|
||||
STEP20D–G PLANNED; production multi-output NOT YET SUPPORTED.
|
||||
|
||||
## Authority and problem
|
||||
|
||||
[Packaging](../PACKAGING.md) remains authoritative: exactly one installed
|
||||
file `/usr/bin/swayfx-mosaic`, no Sway replacement, library overwrite,
|
||||
conflicts/provides/replaces, personal configuration or service.
|
||||
The current production patch has no STEP20C clip dependency. The private
|
||||
SceneFX adds `wlr_scene_tree_set_clip` and `wlr_scene_tree_get_clip` while
|
||||
retaining the stock SONAME. A shared SONAME does not supply missing symbols.
|
||||
No strategy is preapproved; conclusions are evidence, not integration authority.
|
||||
|
||||
## Pinned sources and comparison
|
||||
|
||||
SwayFX 0.6 `fd71a6bdc061bd633b488ae7b83e8a6981d22f86`; private Mosaic
|
||||
`cd550382b311efdbfe3ed892f4e1d281d852e754`; SceneFX 0.5
|
||||
`37ccd723bef49e6891156ffafce8f549f01446cc`; wlroots 0.20.2
|
||||
`d783533489e1f75d6886c2ab5c5960090ef268f8`.
|
||||
Compare installed SceneFX, rebuilt pristine/private SceneFX, retained pristine
|
||||
SwayFX, freshly reconstructed production Mosaic and rebuilt private Mosaic.
|
||||
Preserve ELF headers/dynamic tags, defined/undefined/versioned symbols,
|
||||
pkg-config flags/features, actual compiler headers and loader resolution.
|
||||
Compare all public SceneFX headers and public struct size/alignment/offsets
|
||||
where relevant. Audit the sidecar lifetime and wlroots shared structures.
|
||||
Installed package versions alone never prove exact source provenance or ABI.
|
||||
|
||||
## Budget and isolation
|
||||
|
||||
One stock production reconstruction, one DESTDIR staging, one private
|
||||
SceneFX/Mosaic shared build chain. One optional static SceneFX/Mosaic build
|
||||
only after source audit establishes a bounded Meson method. No compositor
|
||||
launch, graphics, system install, package creation, algorithm or UX change.
|
||||
All source/build/stage/raw evidence stays in a home-directory LAB outside Git.
|
||||
No inherited session sockets, global library path or loader configuration.
|
||||
Loader diagnostics use `ld-linux --list` or `ldd -r` on trusted private
|
||||
artifacts only; these are not compositor execution or graphics tests.
|
||||
Stop implementing any option needing a public ABI, package-contract change,
|
||||
new package or architecture migration; record the required future decision.
|
||||
|
||||
## Scenarios and decision criteria
|
||||
|
||||
S0: apply the unchanged cumulative patch on pinned pristine, prove source
|
||||
equivalence and PKGBUILD checksums, compile with stock dependencies, run only
|
||||
Meson model tests, stage once and inspect the complete manifest.
|
||||
S1: private binary with private versus stock SceneFX; inspect required clip
|
||||
symbols, same SONAME, RUNPATH/RPATH, transitive needs and missing relocations.
|
||||
S2: compare upstream API, private static embedding, isolated distinct-SONAME
|
||||
shared library, and an architecture without a new SceneFX API. Prioritize
|
||||
standard SwayFX coexistence, independent updates, explicit ownership, security
|
||||
maintenance, symbol interposition, licenses and versioned wlroots constraints.
|
||||
|
||||
FEASIBLE requires S0 staging plus characterized incompatibilities and a
|
||||
sufficiently demonstrated candidate without identified ABI risk in the matrix.
|
||||
PARTIALLY_FEASIBLE retains technical/contract/decision gates. INCONCLUSIVE
|
||||
means insufficient reliable comparison. BLOCKED requires an established
|
||||
coexistence violation, not the expected missing private functions in stock.
|
||||
Architecture conclusion remains UNDECIDED until evidence is assessed.
|
||||
|
||||
## Limits
|
||||
|
||||
No graphical, GPU lifetime/cache, all-frame, rotated/scaled/migrated output,
|
||||
installation, update or rollback test. Those are NOT TESTED. The optimized
|
||||
edge property remains a separate shared rendering limitation; native FBO
|
||||
causality remains unproved. No stock-package provenance assertion beyond
|
||||
available metadata/source comparison. All optional strategies require a new
|
||||
explicit integration decision before production work.
|
||||
|
||||
## Completed non-graphical audit (2026-10-13)
|
||||
|
||||
`SWAYFX_MOSAIC_SCENEFX_ABI_PACKAGE_PROBE=PARTIALLY_FEASIBLE`
|
||||
|
||||
Architecture: **SCENEFX_ABI_PACKAGE_REQUIRES_ARCHITECTURAL_DECISION**.
|
||||
No production strategy is selected. The current package passes its separate
|
||||
stock build/stage gate. The naive experimental/stock-library combination is
|
||||
INCOMPATIBLE_WITH_STOCK_SCENEFX. Static embedding is technically demonstrated
|
||||
but retains interposition, shared-wlroots, security and notice gates.
|
||||
|
||||
LAB: `/home/fy59/swayfx-mosaic-abi-package-tp22xfwm/`.
|
||||
Raw commands/stdout/stderr/return codes are in `raw/`; derived evidence is
|
||||
in `results/`. All build artifacts remain external to Git. Sources are fresh
|
||||
archives of verified fixed Git objects, not copies of dirty historical source
|
||||
trees. SceneFX and wlroots object stores were read-only inputs. The pristine
|
||||
SwayFX executable is explicitly reused from the sealed A laboratory, with
|
||||
SHA-256 checked against A's identity; it was not rebuilt or launched.
|
||||
|
||||
## Installed dependencies and source identity
|
||||
|
||||
Installed package metadata: SceneFX `0.5-1`, wlroots0.20 `0.20.2-1`, SwayFX
|
||||
`0.6-1`. pkg-config reports SceneFX **0.5.0** as `scenefx-0.5`, wlroots
|
||||
**0.20.2** as `wlroots-0.20`; generated installed headers and a tiny observer
|
||||
calling native `wlr_version_get_*` agree on runtime wlroots 0.20.2.
|
||||
The `.pc` files reside in `/usr/lib/pkgconfig`, with include roots
|
||||
`/usr/include/scenefx-0.5` and `/usr/include/wlroots-0.20`.
|
||||
All ten queried wlroots build features are true: DRM, X11, libinput, Xwayland,
|
||||
GLES2, Vulkan, GBM, udmabuf, session and color management.
|
||||
|
||||
The 121 non-generated installed wlroots headers exactly match the pinned
|
||||
0.20.2 source archive. Installed generated config/version headers are retained
|
||||
separately. No wlroots source was patched. Header equality and runtime version
|
||||
do not recover the complete distro binary build provenance, patch set or flags;
|
||||
that exact provenance remains **NOT ESTABLISHED**. Other 0.20 builds and future
|
||||
updates are **NOT TESTED**. Compiler dependency output and Meson compile
|
||||
commands/dependencies record the actual headers and library paths used, rather
|
||||
than relying only on declared package versions.
|
||||
|
||||
## S0: unchanged current production package
|
||||
|
||||
`PKGBUILD` remains byte-identical to A. Version `0.6+mosaic.1-1`, x86_64,
|
||||
MIT, SwayFX fixed Git pin, no conflicts/provides/replaces.
|
||||
Depends: cairo, gdk-pixbuf2, libevdev, libinput, json-c, systemd-libs, wayland,
|
||||
libxcb, libxkbcommon, pango, pcre2, scenefx, ttf-font, wlroots0.20.
|
||||
Makedepends: git, meson, ninja, pkgconf, python, wayland-protocols, libdrm, pixman.
|
||||
Checkdepends: kitty, grim. The declared graphical check was deliberately not
|
||||
executed; this ticket forbids compositor launches. `makepkg --printsrcinfo`
|
||||
passed; no package build/install command or dependency installation was used.
|
||||
|
||||
Checksums recomputed without changing the recipe:
|
||||
|
||||
| Input | Expected and actual SHA-256 | Result |
|
||||
| --- | --- | --- |
|
||||
| cumulative production patch | `a04cd06a6ef4fc58e3b40333a53226849e82d9a4757b9ccee58ad7b59a23ff4b` | PASS |
|
||||
| headless IPC source | `0a16c887ca6a87f0392c2eef498b4fad78b9f32afaf7d2a6c0e5e0e37c3c1744` | PASS |
|
||||
| upstream Git source | recipe SKIP plus exact commit check/archive | PASS pinned source; no archive checksum invented |
|
||||
|
||||
The cumulative patch passed forward application on a pristine archive of
|
||||
`fd71a6bdc061bd633b488ae7b83e8a6981d22f86`. Full source equivalence passed
|
||||
for include/, sway/, meson.build, meson_options.txt and tests/mosaic-model.c.
|
||||
The build used the recipe's Meson package flags, prefix `/usr`, bindir `bin`,
|
||||
stock SceneFX and wlroots, `-j 8`. Meson mosaic-model passed 1/1.
|
||||
|
||||
Exactly one `meson install --destdir LAB/stage-current --no-rebuild` ran.
|
||||
Complete non-directory manifest (including symlinks):
|
||||
|
||||
```text
|
||||
/usr/bin/swayfx-mosaic
|
||||
```
|
||||
|
||||
**CURRENT_PACKAGE_STAGE_PASS**. One regular executable ELF64 x86-64, no
|
||||
symlinks, second binary, library, config, dotfile, service or session file.
|
||||
The staged binary has no clip references, no RPATH or RUNPATH; its SceneFX
|
||||
and wlroots references resolve against `/usr/lib`, with no missing relocations.
|
||||
This qualifies only S0 in the measured build/loader/stage matrix. It is not
|
||||
a full Arch release package, graphical test, or STEP20C qualification.
|
||||
|
||||
## ELF comparison
|
||||
|
||||
All compared objects are ELF64 x86-64. Every ELF header, dynamic table, dynamic
|
||||
symbol table, version table, nm listing and objdump output is retained.
|
||||
|
||||
| Object | SONAME or SceneFX dependency | Clip symbols | RPATH / RUNPATH |
|
||||
| --- | --- | --- | --- |
|
||||
| installed SceneFX | `libscenefx-0.5.so` | neither defined | absent |
|
||||
| rebuilt pristine SceneFX | same | neither defined | absent |
|
||||
| rebuilt private SceneFX | same | both defined | absent |
|
||||
| retained pristine SwayFX | DT_NEEDED `libscenefx-0.5.so` | neither required | historical pristine build RUNPATH retained |
|
||||
| staged current Mosaic | DT_NEEDED `libscenefx-0.5.so` | neither required | absent |
|
||||
| shared experimental Mosaic | DT_NEEDED `libscenefx-0.5.so` | both strong undefined | new private SceneFX build RUNPATH |
|
||||
| static experimental Mosaic | no SceneFX DT_NEEDED | both present in ordinary symbol table | absent |
|
||||
| installed wlroots | `libwlroots-0.20.so` | neither defined | absent |
|
||||
|
||||
Exact artifact digests and sizes:
|
||||
|
||||
| Object | Bytes | SHA-256 |
|
||||
| --- | ---: | --- |
|
||||
| stock-binary-elf | 6312256 | `479c55bdbf2cc10e72f4ed8633fc7287d15358ed3f451dd2e6bb350671985af0` |
|
||||
| experimental-binary-elf | 6324344 | `058e51366a71f54ff7824f9a166e3d5cd6797c14f39968ba03587c30bb07bb05` |
|
||||
| static-binary-elf | 6760600 | `ccd47ce5b74c132e19bcd46d022a80e90e8a944acc3db521a89427ee7913c614` |
|
||||
| system-scenefx-symbols | 182216 | `63586ffbe268c6830b5782d5327e8ca3624dbd9b9c9b0e93e591e94ca28a6e62` |
|
||||
| pristine-scenefx-symbols | 493216 | `4e342bbd832ae6e6cfb62938af5bf1fb6b46d30ec50c8b4eea50b652892af19e` |
|
||||
| private-scenefx-symbols | 504792 | `b9582b6c7dd2f903f6de740534f71356261f3e0b84143a24f666f64575b1d91c` |
|
||||
| system-wlroots-elf | 1378496 | `7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837` |
|
||||
| pristine-sway-elf | 6297064 | `8ac865481bc0487344210cd0e16d8e6c7655b29db887cd58ddc11b0ca22fe494` |
|
||||
|
||||
Installed and rebuilt pristine SceneFX have exactly the same exported symbol
|
||||
set. Private SceneFX adds precisely `wlr_scene_tree_set_clip` and
|
||||
`wlr_scene_tree_get_clip`; no stock export is removed. The new functions are
|
||||
unversioned. ELF version requirements for dependency symbols are retained;
|
||||
there is no versioned clip API contract. A common SONAME selects a library
|
||||
identity for DT_NEEDED; it is not evidence that this library implements every
|
||||
undefined function required by the consumer.
|
||||
|
||||
## S1: actual runtime search and relocations
|
||||
|
||||
`ld-linux-x86-64.so.2 --list` inspected library selection without running
|
||||
application main. `ldd -r` on these trusted generated executables checked
|
||||
relocations; return code alone is insufficient because missing-symbol
|
||||
diagnostics can be emitted with status zero. The audit explicitly checks
|
||||
stdout for missing symbols. No command launched the compositor.
|
||||
|
||||
| Consumer / provider | Selected library | Result |
|
||||
| --- | --- | --- |
|
||||
| current staged Mosaic / default | `/usr/lib/libscenefx-0.5.so` | PASS selection/relocations |
|
||||
| experimental / default | LAB/scene-private-build/libscenefx-0.5.so via build RUNPATH | PASS selection/relocations only |
|
||||
| experimental / private process path | same private library | PASS selection/relocations only |
|
||||
| same experimental / stock process path | `/usr/lib/libscenefx-0.5.so` | two missing strong symbols |
|
||||
| static experimental / default | no SceneFX loaded; stock wlroots still loaded | PASS selection/relocations only |
|
||||
|
||||
The stock counterfactual uses the loader's process-local `--library-path
|
||||
/usr/lib` and a separately recorded `LD_LIBRARY_PATH=/usr/lib` only for
|
||||
`ldd -r`. DT_RUNPATH is overridden in this tested setup. No global environment
|
||||
was modified. Build RUNPATH is an absolute temporary path and is not a
|
||||
distribution policy. Moving/removing that private library can expose the
|
||||
stock fallback and its missing functions. No universal loader claim follows
|
||||
for preload, secure execution, another RUNPATH or a changed library graph.
|
||||
The documented search semantics are in [ld.so](https://man7.org/linux/man-pages/man8/ld.so.8.html).
|
||||
|
||||
## Public headers, structures and lifetime
|
||||
|
||||
All seven installed public SceneFX headers match pristine source bytes. The
|
||||
private difference is only two declarations in types/wlr_scene.h. All
|
||||
existing typedefs, enums, callbacks, public struct members and ownership
|
||||
comments in these headers remain byte-identical. Private instrumentation
|
||||
headers are internal, not installed public API. No wlroots header is changed.
|
||||
|
||||
C17 `-Wall -Wextra -Werror` observers report identical size/alignment for 17
|
||||
SceneFX/wlroots structs in stock/pristine/private configurations, plus six
|
||||
selected member offsets. Examples: scene_node 112/8, scene_tree 128/8, scene
|
||||
304/8, scene_buffer 472/8, scene_output 344/8, wlr_output 624/8, output_state
|
||||
208/8, surface 968/8. Full results are in struct-abi-audit.json. This is a
|
||||
measured layout subset; complete semantic ABI compatibility is not proven.
|
||||
|
||||
The retained working patch stores clips in a file-local linked sidecar list
|
||||
in types/scene/wlr_scene.c. Each record holds a tree pointer, copied wlr_box
|
||||
and destroy listener; clearing or node destruction removes the listener and
|
||||
frees the record. Set copies a tree-local rectangle, NULL clears, nonnegative
|
||||
dimensions are asserted; get copies or returns false. No clip field is added
|
||||
to a public struct. Linear lookup, unchecked coordinate addition and silent
|
||||
allocation failure remain private-prototype production design gates. This
|
||||
ticket does not repair them or freeze these API semantics.
|
||||
|
||||
The historical first discarded candidate added a public tree field and
|
||||
crashed; the [spike report](STEP20C_SCENEFX_CLIPPING_SPIKE.md) retains that
|
||||
separate evidence. It must not be conflated with the preserved sidecar patch.
|
||||
No additional current ABI failure beyond the two absent stock functions was
|
||||
demonstrated in this non-graphical matrix. That is not proof of absence of
|
||||
semantic, allocation, concurrency, GPU or future-version defects.
|
||||
|
||||
## Transitive dependencies and symbol ownership
|
||||
|
||||
SceneFX stock/pristine/private share the same direct dependency list:
|
||||
wlroots, wayland-server, libm, libdrm, pixman, EGL, GBM, GLESv2, lcms2 and
|
||||
libc. wlroots additionally depends on Vulkan, libinput, libseat, libudev,
|
||||
display-info, liftoff, Wayland client and XCB components, among others.
|
||||
All DT_NEEDED entries and recursive loader paths are retained; 72 resolved
|
||||
system-library paths map to 50 installed package metadata/license records.
|
||||
No library package was installed.
|
||||
|
||||
SceneFX and wlroots define **85 common dynamic symbols**, including scene,
|
||||
EGL, matrix and color functions. This overlap already exists with stock
|
||||
SceneFX and does not by itself demonstrate a new clipping regression.
|
||||
Controlled `LD_DEBUG=bindings` with relocation tracing shows 34 wlroots
|
||||
scene bindings in each stock/private/static case: 31 go to SceneFX (or the
|
||||
static executable), three remain in wlroots. Thus distinct SONAME or static
|
||||
embedding alone does not isolate the process-wide symbol namespace.
|
||||
The complete binding records distinguish actual provider from matching names.
|
||||
A future design must preserve all shared-struct and function semantics across
|
||||
this graph; hiding/renaming every symbol blindly could break wlroots's scene
|
||||
helper integration. No symbol renaming or visibility experiment is authorized
|
||||
or performed here.
|
||||
|
||||
## S2: static feasibility and license constraints
|
||||
|
||||
The pinned [SceneFX Meson target](https://github.com/wlrfx/scenefx/blob/37ccd723bef49e6891156ffafce8f549f01446cc/meson.build)
|
||||
is a native library() honoring `default_library=static`. One private static
|
||||
SceneFX build and one Mosaic link use its uninstalled pkg-config file; no
|
||||
production Meson edit is needed for this laboratory demonstration.
|
||||
|
||||
The static executable is 6,760,600 bytes versus shared experimental 6,324,344
|
||||
(+436,256 bytes, about 6.90%, both unstripped local builds). It contains both
|
||||
clip implementations as ordinary `T` symbols, not new dynamic exports.
|
||||
DT_NEEDED no longer names SceneFX; EGL/GBM/GLESv2/lcms2 are direct dependencies
|
||||
instead. wlroots remains dynamic, and 85 common exports remain observable.
|
||||
Model and loader checks pass. There is no second staging, strip, package build
|
||||
or graphical run; the one-file ELF shape is demonstrated, a static release
|
||||
package is **NOT TESTED**.
|
||||
|
||||
SwayFX, pinned SceneFX and wlroots use MIT notices. The source license texts
|
||||
and transitive package license metadata are retained. Static copying requires
|
||||
preservation of copyright/permission notices, per the
|
||||
[SceneFX license](https://github.com/wlrfx/scenefx/blob/37ccd723bef49e6891156ffafce8f549f01446cc/LICENSE).
|
||||
A separately installed notice would expand the current exact-one-file manifest.
|
||||
An embedded notice strategy is possible in principle but **NOT TESTED** and
|
||||
requires explicit packaging/legal review; this audit asserts no blanket legal
|
||||
clearance. Embedded SceneFX security fixes require Mosaic rebuilds and an
|
||||
identified maintenance owner. Static embedding does not freeze shared wlroots
|
||||
or make future system updates ABI-safe.
|
||||
|
||||
## Comparative architecture matrix
|
||||
|
||||
| Criterion | A: upstream clipping API | B: static private SceneFX | C: distinct private shared library | D: no new SceneFX API |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Technical evidence | prototype only; acceptance unknown | build/model/relocations PASS | private lookup PASS; distinct identity NOT TESTED | architecture unknown |
|
||||
| System libraries intact | normal upstream update only | yes | yes if isolated | yes |
|
||||
| /usr/bin/sway intact | yes | yes | yes | yes |
|
||||
| Coexistence | supported upstream version required | file ownership preserved; symbol/version qualification pending | extra file and complete lookup/binding contract required | unproved implementation |
|
||||
| Stable clip API | absent today | private | private | no new API, mechanism unproved |
|
||||
| ABI | future release unestablished | measured layouts/relocations pass; semantic/future ABI unestablished | same SONAME naive stock incompatible; isolated design unestablished | unestablished |
|
||||
| Exactly one package file | possible after adoption | ELF shape demonstrated; notices/stage pending | no | possible, not demonstrated |
|
||||
| Upgrade risk | medium shared-version transition | high until wlroots/security policy | medium/high; shared wlroots persists | unknown |
|
||||
| SceneFX maintenance | upstream if accepted | Mosaic embedded-code owner needed | private-library owner needed | stock upstream; alternate mechanism owned by Mosaic |
|
||||
| wlroots maintenance | qualified version pair | shared 0.20 ABI/interposition | shared 0.20 ABI/interposition | stock APIs plus larger Mosaic implementation |
|
||||
| Reproducibility | fixed prototype inputs only | commands/sources retained, bit identity untested | laboratory lookup only | not tested |
|
||||
| Human decision | yes, no submission approved | yes, bundling/security/license contract | yes, new file/SONAME/package ownership | yes, new architecture |
|
||||
|
||||
A is the conditional long-term maintenance direction: one maintained generic
|
||||
primitive rather than perpetual private API patches. It needs maintainer
|
||||
agreement, an ownership/error/bounds contract, version policy and closure of
|
||||
remaining graphical gates. No upstream contact/submission occurred and no
|
||||
acceptance is presumed.
|
||||
B is the most concretely demonstrated candidate that avoids owning a second
|
||||
SceneFX file and removes dependency on the system SceneFX's clip symbols. It
|
||||
does not yet establish safe independent wlroots upgrades or a distributable
|
||||
one-file notice strategy. C separates filesystem ownership and library identity
|
||||
but requires a packaging decision and still shares a symbol namespace.
|
||||
D cannot be called viable: surface crops/output scissor alone do not establish
|
||||
popup-owner rendering, topmost hit traversal, damage, effects, saved buffers,
|
||||
scale/rotation/migration or applied transaction consistency. No alternate
|
||||
clipping engine is developed.
|
||||
|
||||
## Upgrade and hypothetical rollback
|
||||
|
||||
S0 can coexist by file ownership with standard SwayFX. No recipe conflict or
|
||||
replacement blocks its updates. That does not guarantee an old Mosaic binary
|
||||
will load after every shared SceneFX/wlroots upgrade. Requalify and rebuild
|
||||
Mosaic separately; do not pin or replace system libraries to force it to work.
|
||||
Static SceneFX makes Mosaic independent of system SceneFX updates, but an
|
||||
embedded security fix requires its own rebuild and shared wlroots remains a
|
||||
constraint. A private shared library likewise needs its own security/rebase
|
||||
policy and explicit transitive compatibility.
|
||||
|
||||
Hypothetical removal of S0 deletes only /usr/bin/swayfx-mosaic and leaves
|
||||
/usr/bin/sway, SceneFX and wlroots untouched. C would additionally remove only
|
||||
its approved private library path. Real install, upgrade, uninstall and
|
||||
rollback are NOT TESTED and were not attempted.
|
||||
|
||||
## Evidence-backed answers Q1–Q18
|
||||
|
||||
| Question | Answer |
|
||||
| --- | --- |
|
||||
| Q1 current stock build/stage? | Yes: clean pinned source/patch equivalence, stock Meson build and single DESTDIR stage PASS. |
|
||||
| Q2 exact manifest? | Yes, exactly one regular ELF /usr/bin/swayfx-mosaic, no symlink. |
|
||||
| Q3 added symbols? | Exactly wlr_scene_tree_set_clip and wlr_scene_tree_get_clip, no removed export. |
|
||||
| Q4 required? | Both strong undefined in the shared private Mosaic binary; both ordinary definitions in static. |
|
||||
| Q5 SONAME? | Names a provider, does not certify its export set; stock selection proves the two functions missing. |
|
||||
| Q6 public layouts? | No member/type/enum/callback change in retained SceneFX headers, no wlroots source change; 17 sizes/alignments and six offsets identical. |
|
||||
| Q7 further ABI incompatibility? | None newly demonstrated in current non-graphical matrix; 85 overlaps and mixed scene bindings are observed constraints, not proof of a new crash. Historical discarded struct-field candidate did crash separately. |
|
||||
| Q8 stock with private binary? | No: missing strong clip relocations; unmodified stock cannot satisfy the tested binary. |
|
||||
| Q9 static? | Build/model/loader shape passes with no SceneFX DT_NEEDED; one-file distribution still needs notice, security and wlroots decisions. |
|
||||
| Q10 isolated shared? | Separate file/SONAME/RUNPATH could isolate library selection; process-wide symbol interactions remain. Distinct-SONAME build NOT TESTED. |
|
||||
| Q11 package changes? | A needs approved supported API/version dependency; B approved embedding and notices/security/build policy; C extra file/identity/lookup contract; D architecture decision without necessarily another file. None applied. |
|
||||
| Q12 update independence? | B removes shared SceneFX dependency and preserves file independence most concretely here; shared wlroots still prevents unconditional update-safety claims. |
|
||||
| Q13 long-term maintenance? | A conditionally, if officially accepted/maintained; no acceptance evidence. Otherwise B/C need explicit project owners for security and rebases. |
|
||||
| Q14 upstream preferable? | Conditional maintenance inference from one shared maintained primitive; error/bounds/ABI policy, ownership and graphical gates still missing. No submission authorized. |
|
||||
| Q15 GPU/caches/version risks? | Not measured here; all-frame damage, references, effects and cache semantics must be separately qualified across actual version pairs. |
|
||||
| Q16 shared edge phenomenon? | Separate rendering limitation carried across all options; static linkage does not repair it or prove native causality. |
|
||||
| Q17 choose now? | Evidence supports candidate comparison only; explicit user architecture/packaging decision required before integration. |
|
||||
| Q18 smallest next work? | Non-graphical contract dossier for static symbol binding/ownership, version qualification, security owner and one-file notices, plus an upstream API proposal dossier without submission. No new renderer or production patch. |
|
||||
|
||||
## Budget, validation and limits
|
||||
|
||||
PASS: one stock reconstruction, one stock stage, one private shared chain,
|
||||
one optional static chain, one rebuilt pristine SceneFX reference. All Meson
|
||||
setup/compile commands returned zero; mosaic-model 1/1 in stock, private
|
||||
shared and static builds. C17 layout observers pass. AST, shell syntax, patch
|
||||
application/equivalence, recipe checksums, export/symbol/header/loader and
|
||||
license inventories are checked independently of graphical evidence.
|
||||
|
||||
Zero compositor launches, graphical runs, installations and packages generated.
|
||||
NOT RUN: graphical headless IPC suite, GPU readback, physical backend/scanout,
|
||||
new geometry/caches/lifetimes, actual update/rollback, another wlroots binary
|
||||
version, bit-reproducible builds, static staging, distinct-SONAME build or
|
||||
upstream submission. Preparation guards were tightened after the single build
|
||||
chain; LAB/prepare-executed.py preserves the prior executed version and command
|
||||
logs. No build was repeated for that non-behavioral fixture hardening.
|
||||
|
||||
The report and source fixture remain local and uncommitted. No source Mosaic
|
||||
production, PKGBUILD, cumulative patch, UX, AGENTS, source pin or installed
|
||||
compositor/library was changed. Final integrity and manifest details follow.
|
||||
|
||||
## Final integrity and local handoff
|
||||
|
||||
Source inventory: **1,463 entries**, SHA-256
|
||||
`a27fa33d57d3423e09f02430f04cfed643762d876099b7f88489229115c4a1e5`.
|
||||
Evidence inventory: **204 entries**, SHA-256
|
||||
`86f7654c8ad962053644ce2c4c13eb9f026d6618b171e0cc3a8fc3034eb380d5`.
|
||||
Both were sealed once and every entry independently checked again.
|
||||
Original A source/evidence manifests remain byte-identical and pass with the
|
||||
explicit historical README snapshot mapping.
|
||||
|
||||
Final normal checks PASS: AST for all four B Python scripts; shell syntax
|
||||
for the existing exporter and PKGBUILD; C17 observers; all recorded builds and
|
||||
three model tests; patch application/source equivalence; recipe digests;
|
||||
Markdown local links; complete tracked/new-file whitespace/source-only audit;
|
||||
protected production/UX/AGENTS/package/pin paths; installed sway/SceneFX/wlroots
|
||||
hash equality; absence of LAB processes and sockets. The deep closeout review
|
||||
was performed by the parent, not by an independent subagent; its findings and
|
||||
fixture hardening are recorded in results/closeout-review.json.
|
||||
|
||||
Local HEAD and directly queried origin/main and github/main remain
|
||||
`7c22b4020edc2d9ab6779d789159937523aae75a`. A was published; B has no staged
|
||||
files, commit or push. The final worktree intentionally contains only the B
|
||||
report/fixture and synchronized Architecture, Known Limitations, Roadmap,
|
||||
README, Packaging audit section and Changelog. Packaging authority is unchanged.
|
||||
Complete B diff, including untracked files, is retained as LAB/complete-phase-b.diff.
|
||||
This report/diff is outside the sealed scientific inventory to avoid digest cycles.
|
||||
|
||||
No system install, package distribution, library replacement, live-session IPC,
|
||||
QuickShell/dotfile/Codex change or STEP20D work occurred. No experimental process
|
||||
or private socket remains. STEP20C NEXT; production multi-output NOT YET SUPPORTED.
|
||||
|
||||
## ABI/package evidence freeze — new ticket phase A
|
||||
|
||||
The preceding local handoff records the historical campaign state and its
|
||||
A/B labels. This freeze publishes those ABI/package findings, without
|
||||
reclassification, new builds, staging or compositor execution. The new static
|
||||
distribution-contract phase B is separate and is not part of this commit.
|
||||
|
||||
All 1,463 source and 204 evidence entries were verified before changing the
|
||||
fixture README's publication wording. Historical manifests are immutable.
|
||||
For later verification, map only the historical fixture README path to:
|
||||
|
||||
`/home/fy59/swayfx-mosaic-abi-freeze-i6x1osil/historical-fixture-README.md`
|
||||
|
||||
The snapshot retains the hash in the original manifest; all other paths must
|
||||
be checked directly. The four Python scripts are unchanged. Freeze audit
|
||||
commands and historical-result verification are retained in `/home/fy59/swayfx-mosaic-abi-freeze-i6x1osil`.
|
||||
Current package remains CURRENT_PACKAGE_STAGE_PASS; private shared with stock
|
||||
SceneFX remains INCOMPATIBLE_WITH_STOCK_SCENEFX; static remains build/loader
|
||||
evidence with no historical static staging or graphical qualification.
|
||||
|
||||
`SWAYFX_MOSAIC_SCENEFX_ABI_PACKAGE_PROBE=PARTIALLY_FEASIBLE`
|
||||
|
||||
`SCENEFX_ABI_PACKAGE_REQUIRES_ARCHITECTURAL_DECISION`
|
||||
|
||||
No distribution architecture or production integration is authorized.
|
||||
85
tooling/probes/step20c-scenefx-abi-package/README.md
Normal file
85
tooling/probes/step20c-scenefx-abi-package/README.md
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# SceneFX ABI/package audit fixture
|
||||
|
||||
**EXPERIMENTAL / PRESERVED EVIDENCE / no production integration.**
|
||||
Read the [protocol](../../../docs/design/STEP20C_SCENEFX_ABI_PACKAGE_FEASIBILITY.md).
|
||||
Private sources/builds/results must stay outside Git. No script may launch a
|
||||
compositor, use desktop IPC, install system files or change package contracts.
|
||||
Budgets: one stock reconstruction/stage, one private shared chain, optionally
|
||||
one static chain after source inspection. ELF checks are diagnostics only.
|
||||
Generated artifacts and manifests stay in LAB.
|
||||
|
||||
## Build-only reproduction
|
||||
|
||||
Use a full Mosaic clone containing the fixed commits, installed stock build
|
||||
prerequisites and local Git object stores containing pinned SceneFX and wlroots.
|
||||
`prepare.py` does not fetch or install missing dependencies. It archives exact
|
||||
objects, applies retained private patches in new copies, and logs each bounded
|
||||
build command. Create a new LAB under your home directory, outside this clone.
|
||||
A LAB can be prepared only once; a failed preparation consumes that attempt.
|
||||
Do not bypass the ticket's build budget with another directory.
|
||||
|
||||
```sh
|
||||
python tooling/probes/step20c-scenefx-abi-package/prepare.py LAB \
|
||||
--scenefx-git /path/to/scenefx-object-store \
|
||||
--wlroots-git /path/to/wlroots-object-store --static
|
||||
python tooling/probes/step20c-scenefx-abi-package/audit.py LAB
|
||||
python tooling/probes/step20c-scenefx-abi-package/summarize.py LAB
|
||||
```
|
||||
|
||||
`--static` is optional and requires the source-audit decision documented in
|
||||
this ticket. `--pristine-binary` optionally accepts only the A campaign's
|
||||
SHA-256-qualified retained pristine executable; otherwise its ELF comparison
|
||||
is NOT TESTED. A newly reproduced baseline binary needs its own provenance
|
||||
qualification, not substitution under that historical identity.
|
||||
The retained completed LAB is `/home/fy59/swayfx-mosaic-abi-package-tp22xfwm`.
|
||||
Its optional static trial was exercised once; no new run/build is authorized
|
||||
by these instructions for this completed ticket.
|
||||
|
||||
`audit.py` checks complete stage entries including symlinks, recipe digests,
|
||||
ELF headers/symbols/version requirements, loader paths and relocations, public
|
||||
header differences, C17 struct-layout observers and compiler dependencies.
|
||||
It runs only diagnostic loader modes and small non-graphical layout/version
|
||||
observers. It never runs a compositor entry point. `ldd -r` diagnostics are
|
||||
checked even when its status is zero. Its stock-versus-private process library
|
||||
paths are temporary and recorded. Required tools: cc, Meson/Ninja, pkg-config,
|
||||
readelf, nm, objdump, the native x86-64 glibc loader, ldd and read-only Arch
|
||||
package metadata (`pacman -Qi`; no package installation). Other platforms are
|
||||
NOT TESTED, rather than silently inferred compatible.
|
||||
|
||||
`summarize.py` records measured
|
||||
S0/S1 results separately from inferred architectural options. Static loader
|
||||
success does not establish graphics, semantic ABI, future updates or licensing
|
||||
clearance. All generated JSON/logs/source archives/builds remain in LAB.
|
||||
|
||||
## Completed result
|
||||
|
||||
Current package: **CURRENT_PACKAGE_STAGE_PASS**, exactly one regular ELF
|
||||
`/usr/bin/swayfx-mosaic`, stock dynamic dependencies, no clip references.
|
||||
Shared private binary with stock SceneFX: **INCOMPATIBLE_WITH_STOCK_SCENEFX**,
|
||||
two missing strong clip symbols despite the common SONAME. Shared private
|
||||
resolution and static embedding pass non-graphical build/model/loader checks.
|
||||
Seventeen layouts and six selected offsets match; 85 exports overlap wlroots.
|
||||
Stock/private/static wlroots scene bindings are retained, including the mix
|
||||
between SceneFX providers and wlroots helpers. Distinct private SONAME,
|
||||
future-version safety, GPU lifetimes and graphical behavior are NOT TESTED.
|
||||
|
||||
Overall **PARTIALLY_FEASIBLE**; architecture
|
||||
**SCENEFX_ABI_PACKAGE_REQUIRES_ARCHITECTURAL_DECISION**. No packaging option is
|
||||
adopted. The historical campaign is closed; STEP20C NEXT and production multi-output
|
||||
NOT YET SUPPORTED. `seal.py LAB` refuses existing manifests; `seal.py LAB
|
||||
--verify` verifies all entries without regenerating. Derive all evidence and
|
||||
finalize fixtures before sealing. Report and complete diff are captured outside
|
||||
the sealed inventory to avoid self-referential digest dependencies.
|
||||
|
||||
## ABI evidence freeze
|
||||
|
||||
The October 2026 freeze verifies the original 1,463 source entries and 204
|
||||
evidence entries without rerunning the campaign. Their manifest hashes remain
|
||||
`a27fa33d57d3423e09f02430f04cfed643762d876099b7f88489229115c4a1e5` and
|
||||
`86f7654c8ad962053644ce2c4c13eb9f026d6618b171e0cc3a8fc3034eb380d5`.
|
||||
This README's publication wording changed after verification. To verify the
|
||||
sealed historical source manifest subsequently, map only its README entry to
|
||||
the exact pre-freeze snapshot recorded in the report. Do not regenerate the
|
||||
manifest or substitute other source files. Python fixture bytes are unchanged.
|
||||
The old report's A/B labels refer to its earlier ticket, not the new static
|
||||
distribution-contract phase. That later phase must remain local.
|
||||
207
tooling/probes/step20c-scenefx-abi-package/audit.py
Normal file
207
tooling/probes/step20c-scenefx-abi-package/audit.py
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Read-only ELF/header/package audit of prepared trusted private artifacts.
|
||||
|
||||
WHY: record missing relocations, symbol overlap and header provenance separately.
|
||||
CONTRACT: no compositor entry point executes; loader commands only inspect ELF.
|
||||
INVARIANT: results may be derived again before sealing, never after sealing.
|
||||
"""
|
||||
import argparse
|
||||
import difflib
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
|
||||
p = argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument('lab', type=Path)
|
||||
a = p.parse_args(); lab = a.lab.resolve(); repo = Path(__file__).resolve().parents[3]
|
||||
assert lab.is_dir() and lab.is_relative_to(Path.home()) and lab != Path.home()
|
||||
assert not lab.is_relative_to(repo)
|
||||
assert not (lab/'results/evidence-sha256.txt').exists(), 'sealed evidence; verify, never rewrite'
|
||||
results = lab/'results'; raw = lab/'raw'
|
||||
env = {k: v for k, v in os.environ.items() if k not in (
|
||||
'SWAYSOCK', 'I3SOCK', 'WAYLAND_DISPLAY', 'DISPLAY', 'XDG_RUNTIME_DIR',
|
||||
'LD_LIBRARY_PATH', 'LD_PRELOAD', 'PKG_CONFIG_PATH')}
|
||||
env['LC_ALL'] = 'C'
|
||||
|
||||
def sha(path): return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
def save(name, value): (results/(name+'.json')).write_text(json.dumps(value, indent=2)+'\n')
|
||||
def run(label, args, extra=None):
|
||||
proc = subprocess.run(list(map(str, args)), env={**env, **(extra or {})},
|
||||
capture_output=True, text=True, timeout=60)
|
||||
entry = {'args': list(map(str, args)), 'env_override': extra or {},
|
||||
'returncode': proc.returncode, 'stdout': proc.stdout, 'stderr': proc.stderr}
|
||||
(raw/(label+'.json')).write_text(json.dumps(entry, indent=2)+'\n')
|
||||
return entry
|
||||
|
||||
def elf(label, path):
|
||||
path = path.resolve(); parts = {}
|
||||
assert path.stat().st_size <= 128*1024*1024, 'ELF input exceeds audit bound'
|
||||
for tag, args in [('header', ['readelf','-hW']), ('dynamic',['readelf','-dW']),
|
||||
('symbols',['readelf','--dyn-syms','-W']), ('versions',['readelf','-VW']),
|
||||
('nm',['nm','-D','--format=posix']), ('nm-all',['nm','--format=posix']), ('objdump',['objdump','-p'])]:
|
||||
parts[tag] = run(label+'-'+tag, [*args, path])
|
||||
assert parts[tag]['returncode']==0, (label,tag)
|
||||
defs = []; undefs = []; weak = []
|
||||
for line in parts['symbols']['stdout'].splitlines():
|
||||
fields = line.split()
|
||||
if len(fields) >= 8 and fields[0].rstrip(':').isdigit():
|
||||
name = fields[7]; base = name.split('@')[0]
|
||||
if fields[6]=='UND':
|
||||
(weak if fields[4]=='WEAK' else undefs).append(base)
|
||||
elif fields[4] in ('GLOBAL','WEAK'): defs.append(base)
|
||||
d = parts['dynamic']['stdout']
|
||||
result = {'path':str(path), 'sha256':sha(path), 'bytes':path.stat().st_size,
|
||||
'class':re.search(r'Class:\s+(.*)',parts['header']['stdout'])[1],
|
||||
'machine':re.search(r'Machine:\s+(.*)',parts['header']['stdout'])[1],
|
||||
'SONAME':re.findall(r'\(SONAME\).*?\[(.*?)\]',d),
|
||||
'DT_NEEDED':re.findall(r'\(NEEDED\).*?\[(.*?)\]',d),
|
||||
'RPATH':re.findall(r'\(RPATH\).*?\[(.*?)\]',d),
|
||||
'RUNPATH':re.findall(r'\(RUNPATH\).*?\[(.*?)\]',d),
|
||||
'defined':sorted(set(defs)), 'undefined':sorted(set(undefs)),
|
||||
'weak_undefined':sorted(set(weak)),
|
||||
'clip_defined':[x for x in defs if x in clips],
|
||||
'clip_undefined':[x for x in undefs if x in clips]}
|
||||
save(label,result);return result
|
||||
clips = ['wlr_scene_tree_set_clip','wlr_scene_tree_get_clip']
|
||||
versions = {}
|
||||
for pkg in ['scenefx-0.5','wlroots-0.20','wayland-server','libdrm','pixman-1']:
|
||||
versions[pkg] = {op:run('pkg-'+pkg+'-'+op,['pkg-config','--'+op,pkg])['stdout'].strip()
|
||||
for op in ('modversion','cflags','libs')}
|
||||
for pkg in ['scenefx-0.5','wlroots-0.20']:
|
||||
path=Path('/usr/lib/pkgconfig')/(pkg+'.pc');sh=(raw/(pkg+'.pc'));sh.write_bytes(path.read_bytes())
|
||||
features = {name:run('feature-'+name,['pkg-config','--variable=have_'+name,'wlroots-0.20'])['stdout'].strip()
|
||||
for name in ['drm_backend','x11_backend','libinput_backend','xwayland',
|
||||
'gles2_renderer','vulkan_renderer','gbm_allocator','udmabuf_allocator','session','color_management']}
|
||||
save('dependency-versions',{'packages':versions,'wlroots_features':features})
|
||||
# Metadata is evaluated in a non-installing shell: functions are defined but not called.
|
||||
meta = run('pkgbuild-metadata',['bash','-c','startdir=$2; source "$1"; declare -p pkgname pkgver pkgrel arch license depends makedepends checkdepends source sha256sums _upstream_commit; declare -p conflicts provides replaces 2>/dev/null || true','audit',repo/'PKGBUILD',repo])
|
||||
assert meta['returncode']==0
|
||||
save('pkgbuild-metadata',meta)
|
||||
pkgtext=(repo/'PKGBUILD').read_text()
|
||||
expected=re.findall(r"'([a-f0-9]{64})'",pkgtext)
|
||||
paths=[repo/'patches/0001-mosaic-layout-viewport.patch',repo/'tests/headless-ipc.py']
|
||||
checks=[{'path':str(path),'expected':exp,'actual':sha(path),'PASS':sha(path)==exp} for path,exp in zip(paths,expected)]
|
||||
assert len(checks)==2 and all(x['PASS'] for x in checks)
|
||||
save('package-source-checksums',checks)
|
||||
assert not re.search(r'^\s*(conflicts|provides|replaces)=',pkgtext,re.M)
|
||||
save('package-contract',{'authority':'docs/PACKAGING.md','expected':['/usr/bin/swayfx-mosaic'],
|
||||
'conflicts_provides_replaces':False,'stock_scene_dependency':True,'STEP20C':False})
|
||||
stage=lab/'stage-current'
|
||||
entries=[]
|
||||
for path in sorted(stage.rglob('*')):
|
||||
if path.is_symlink() or not path.is_dir():
|
||||
entries.append({'path':'/'+str(path.relative_to(stage)), 'symlink':path.is_symlink(),
|
||||
'executable':os.access(path,os.X_OK),'sha256':sha(path) if path.is_file() else None})
|
||||
assert len(entries)==1 and entries[0]['path']=='/usr/bin/swayfx-mosaic' and not entries[0]['symlink'] and entries[0]['executable']
|
||||
save('stock-stage-manifest',{'status':'CURRENT_PACKAGE_STAGE_PASS','entries':entries})
|
||||
paths={'system-scenefx-symbols':Path('/usr/lib/libscenefx-0.5.so'),
|
||||
'system-wlroots-elf':Path('/usr/lib/libwlroots-0.20.so'),
|
||||
'pristine-scenefx-symbols':lab/'scene-pristine-build/libscenefx-0.5.so',
|
||||
'private-scenefx-symbols':lab/'scene-private-build/libscenefx-0.5.so',
|
||||
'stock-binary-elf':stage/'usr/bin/swayfx-mosaic',
|
||||
'experimental-binary-elf':lab/'private-build/sway/swayfx-mosaic'}
|
||||
if (lab/'retained-pristine-sway').exists():paths['pristine-sway-elf']=lab/'retained-pristine-sway'
|
||||
if (lab/'static-build/sway/swayfx-mosaic').exists():paths['static-binary-elf']=lab/'static-build/sway/swayfx-mosaic'
|
||||
elves={label:elf(label,path) for label,path in paths.items()}
|
||||
stockdefs=set(elves['system-scenefx-symbols']['defined']);privdefs=set(elves['private-scenefx-symbols']['defined'])
|
||||
wlrootsdefs=set(elves['system-wlroots-elf']['defined'])
|
||||
save('symbol-comparison',{'private_added':sorted(privdefs-stockdefs),'private_removed':sorted(stockdefs-privdefs),
|
||||
'scene_wlroots_overlap':sorted(privdefs & wlrootsdefs),
|
||||
'pristine_system_added':sorted(set(elves['pristine-scenefx-symbols']['defined'])-stockdefs),
|
||||
'pristine_system_removed':sorted(stockdefs-set(elves['pristine-scenefx-symbols']['defined']))})
|
||||
assert set(clips)<=privdefs and not set(clips)&stockdefs
|
||||
assert set(clips)<=set(elves['experimental-binary-elf']['undefined'])
|
||||
save('unresolved-symbols',{'stock_missing':clips,'experimental_requires':clips,'classification':'INCOMPATIBLE_WITH_STOCK_SCENEFX'})
|
||||
# ld-linux --list never enters the application. Stock --library-path overrides
|
||||
# DT_RUNPATH on this artifact; verify the selected absolute library in raw output.
|
||||
loader='/usr/lib/ld-linux-x86-64.so.2';loads={}
|
||||
for label,path in [('stock',paths['stock-binary-elf']),('private',paths['experimental-binary-elf'])]:
|
||||
loads[label+'-default']=run('loader-'+label+'-default',[loader,'--list',path])
|
||||
for label,directory in [('private',lab/'scene-private-build'),('stock',Path('/usr/lib'))]:
|
||||
loads['experimental-'+label]=run('loader-experimental-'+label,[loader,'--library-path',directory,'--list',paths['experimental-binary-elf']])
|
||||
loads['relocations-'+label]=run('relocations-'+label,['ldd','-r',paths['experimental-binary-elf']],{'LD_LIBRARY_PATH':str(directory)})
|
||||
loads['stock-bindings']=run('stock-bindings',['ldd','-r',paths['stock-binary-elf']],{'LD_DEBUG':'bindings'})
|
||||
loads['stock-relocations']=run('stock-relocations',['ldd','-r',paths['stock-binary-elf']])
|
||||
if 'static-binary-elf' in paths:
|
||||
loads['static-default']=run('loader-static-default',[loader,'--list',paths['static-binary-elf']])
|
||||
loads['static-relocations']=run('static-relocations',['ldd','-r',paths['static-binary-elf']])
|
||||
assert '/usr/lib/libscenefx-0.5.so' in loads['experimental-stock']['stdout']
|
||||
assert all('undefined symbol: '+c in loads['relocations-stock']['stdout'] for c in clips)
|
||||
assert 'undefined symbol:' not in loads['relocations-private']['stdout']
|
||||
assert 'undefined symbol:' not in loads['stock-relocations']['stdout']
|
||||
if 'static-relocations' in loads: assert 'undefined symbol:' not in loads['static-relocations']['stdout']
|
||||
if 'static-binary-elf' in paths:
|
||||
loads['static-bindings']=run('static-bindings',['ldd','-r',paths['static-binary-elf']],{'LD_DEBUG':'bindings'})
|
||||
loads['private-bindings']=run('private-bindings',['ldd','-r',paths['experimental-binary-elf']],{'LD_DEBUG':'bindings','LD_LIBRARY_PATH':str(lab/'scene-private-build')})
|
||||
run('installed-package-metadata',['pacman','-Qi','scenefx','wlroots0.20','swayfx'])
|
||||
save('loader-resolution',loads)
|
||||
bindings={}
|
||||
for name in ['stock-bindings','private-bindings','static-bindings']:
|
||||
if name in loads:
|
||||
lines=[line.strip() for line in loads[name]['stdout'].splitlines()
|
||||
if 'binding file /usr/lib/libwlroots' in line and 'wlr_scene_' in line]
|
||||
bindings[name]={'total':len(lines),
|
||||
'external_provider':sum(' to /usr/lib/libwlroots' not in line for line in lines),
|
||||
'self_wlroots':sum(' to /usr/lib/libwlroots' in line for line in lines),
|
||||
'lines':lines}
|
||||
save('scene-symbol-bindings',bindings)
|
||||
# All installed public headers are compared; generated config headers are reported
|
||||
# separately rather than mistaken for source changes.
|
||||
stockheaders=Path('/usr/include/scenefx-0.5/scenefx')
|
||||
headeraudit=[]
|
||||
for installed in sorted(stockheaders.rglob('*.h')):
|
||||
rel=installed.relative_to(stockheaders);pristine=lab/'scene-pristine/include/scenefx'/rel;private=lab/'scene-private/include/scenefx'/rel
|
||||
if not pristine.exists():
|
||||
headeraudit.append({'path':str(rel),'generated_or_missing_source':True,'installed_sha256':sha(installed)});continue
|
||||
diff=''.join(difflib.unified_diff(pristine.read_text().splitlines(True),private.read_text().splitlines(True),fromfile='pristine/'+str(rel),tofile='private/'+str(rel)))
|
||||
headeraudit.append({'path':str(rel),'system_pristine_equal':installed.read_bytes()==pristine.read_bytes(),
|
||||
'pristine_private_equal':pristine.read_bytes()==private.read_bytes(),'diff':diff,
|
||||
'sha256':{'installed':sha(installed),'pristine':sha(pristine),'private':sha(private)}})
|
||||
save('public-header-diff',headeraudit)
|
||||
# Compile tiny C17 layout observers, not scene/compositor instances. Size/offset
|
||||
# equality is a measured necessary condition, never a full ABI guarantee.
|
||||
structs=['wlr_scene_node','wlr_scene_tree','wlr_scene','wlr_scene_surface','wlr_scene_rect','wlr_scene_shadow','wlr_scene_blur','wlr_scene_optimized_blur','wlr_scene_buffer','wlr_scene_output','wlr_scene_timer','wlr_scene_layer_surface_v1','wlr_output','wlr_output_state','wlr_surface','wlr_buffer','wlr_renderer']
|
||||
source='#define WLR_USE_UNSTABLE\n#include <stddef.h>\n#include <stdio.h>\n#include <scenefx/types/wlr_scene.h>\n#include <wlr/types/wlr_output.h>\n#include <wlr/types/wlr_compositor.h>\n#include <wlr/types/wlr_buffer.h>\n#include <wlr/version.h>\nint main(void) {\nprintf("wlroots_runtime %d.%d.%d\\n",wlr_version_get_major(),wlr_version_get_minor(),wlr_version_get_micro());\n'
|
||||
for struct in structs:source+=f'printf("{struct} %zu %zu\\n", sizeof(struct {struct}), _Alignof(struct {struct}));\n'
|
||||
for struct,field in [('wlr_scene_tree','children'),('wlr_scene','tree'),('wlr_scene','outputs'),('wlr_scene_node','parent'),('wlr_scene_buffer','buffer'),('wlr_scene_output','output')]:
|
||||
source+=f'printf("offset:{struct}.{field} %zu\\n", offsetof(struct {struct},{field}));\n'
|
||||
source+='return 0; }\n'; (lab/'struct-layout.c').write_text(source)
|
||||
flags=shlex.split(run('layout-pkg-cflags',['pkg-config','--cflags','wlroots-0.20','wayland-server','pixman-1'])['stdout'])
|
||||
layouts={}
|
||||
for name,inc in [('system',Path('/usr/include/scenefx-0.5')),('pristine',lab/'scene-pristine/include'),('private',lab/'scene-private/include')]:
|
||||
exe=lab/('layout-'+name)
|
||||
build=run('layout-compile-'+name,['cc','-std=c17','-Wall','-Wextra','-Werror','-I'+str(inc),*flags,lab/'struct-layout.c','-o',exe,'-lwlroots-0.20'])
|
||||
assert build['returncode']==0,build['stderr']
|
||||
layouts[name]=run('layout-run-'+name,[exe])['stdout']
|
||||
assert len(set(layouts.values()))==1
|
||||
save('struct-abi-audit',{'layouts':layouts,'size_alignment_offsets':'IDENTICAL in 17 structs / 6 offsets',
|
||||
'complete_ABI':'NOT ESTABLISHED; layout equality alone insufficient',
|
||||
'private_storage':'destroy-listener-owned sidecar; no public field additions'})
|
||||
wlheaders=[]
|
||||
for src in sorted((lab/'wlroots-source/include/wlr').rglob('*.h')):
|
||||
rel=src.relative_to(lab/'wlroots-source/include/wlr');installed=Path('/usr/include/wlroots-0.20/wlr')/rel
|
||||
wlheaders.append({'path':str(rel),'installed':installed.exists(),'equal':installed.exists() and src.read_bytes()==installed.read_bytes(), 'source_sha256':sha(src),'installed_sha256':sha(installed) if installed.exists() else None})
|
||||
required={label:sorted(set(info['undefined']) & wlrootsdefs) for label,info in elves.items() if label!='system-wlroots-elf'}
|
||||
save('wlroots-compatibility',{'pkg_config':'wlroots-0.20','version':'0.20.2','source_pin':'d783533489e1f75d6886c2ab5c5960090ef268f8','headers':wlheaders,'required_symbols_provided_by_installed_wlroots':required,'exact_installed_binary_source_provenance':'NOT ESTABLISHED by version/headers alone','future_0_20_ABI':'NOT ESTABLISHED'})
|
||||
# Compiler dependency files contain headers actually read, not inferred flags.
|
||||
for build in ['stock-build','private-build','static-build','scene-private-build']:
|
||||
if (lab/build).exists():run('compiler-headers-'+build,['ninja','-C',lab/build,'-t','deps'])
|
||||
static=elves.get('static-binary-elf')
|
||||
save('static-link-feasibility',{'status':'BUILD_AND_LOADER_PASS' if static else 'NOT TESTED',
|
||||
'scene_DT_NEEDED_absent':static is not None and 'libscenefx-0.5.so' not in static['DT_NEEDED'],
|
||||
'wlroots_still_dynamic':static is not None and 'libwlroots-0.20.so' in static['DT_NEEDED'],
|
||||
'clip_dynamic_defined':static['clip_defined'] if static else [],
|
||||
'clip_static_symbols':[line for line in run('static-clip-symbols',['nm','--format=posix',paths['static-binary-elf']])['stdout'].splitlines() if line.split()[0] in clips] if static else [],
|
||||
'overlapping_dynamic_exports':sorted(set(static['defined']) & wlrootsdefs) if static else [],
|
||||
'unique_file_possible':'ELF shape demonstrated; no second stage performed',
|
||||
'licenses':'MIT notices must accompany substantial copied code; embedded notice strategy NOT TESTED',
|
||||
'update_safety':'NOT ESTABLISHED; executable interposes shared wlroots scene symbols'})
|
||||
save('private-shared-feasibility',{'status':'PROCESS_LOCAL_LOADER_PASS; distinct SONAME packaging NOT TESTED',
|
||||
'same_SONAME':'unsafe naive substitution; default build RUNPATH is private LAB, not distributable',
|
||||
'future_requirements':['distinct SONAME and DT_NEEDED','private installation path','controlled RUNPATH','audit all transitive scene consumers and interposition','security/version owner'],
|
||||
'current_one_file_contract':'would require new decision for additional library artifact'})
|
||||
print('PASS stock stage, ELF/loader comparisons, missing clip proof, C17 17-struct layouts and header audit')
|
||||
134
tooling/probes/step20c-scenefx-abi-package/prepare.py
Normal file
134
tooling/probes/step20c-scenefx-abi-package/prepare.py
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#!/usr/bin/env python3
|
||||
"""One-shot private source/build/stage reconstruction; never runs a compositor.
|
||||
|
||||
CONTRACT: empty external LAB, explicit local Git object stores at fixed commits.
|
||||
INVARIANT: every command is logged; no install except stock DESTDIR staging.
|
||||
WHY: a successful experimental link must never stand in for stock packaging.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import tarfile
|
||||
|
||||
p = argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument('lab', type=Path)
|
||||
p.add_argument('--scenefx-git', type=Path, required=True)
|
||||
p.add_argument('--wlroots-git', type=Path, required=True)
|
||||
p.add_argument('--pristine-binary', type=Path)
|
||||
p.add_argument('--static', action='store_true')
|
||||
a = p.parse_args()
|
||||
lab = a.lab.resolve()
|
||||
repo = Path(__file__).resolve().parents[3]
|
||||
assert lab.is_dir() and lab.is_relative_to(Path.home()) and lab != Path.home()
|
||||
assert not lab.is_relative_to(repo) and not (lab/'stock').exists()
|
||||
assert not (lab/'prepare-started.json').exists(), 'budget consumed; inspect logs, never restart'
|
||||
(lab/'prepare-started.json').write_text(json.dumps(vars(a), default=str, indent=2)+'\n')
|
||||
raw = lab/'raw'; raw.mkdir(exist_ok=True)
|
||||
results = lab/'results'; results.mkdir(exist_ok=True)
|
||||
env = {k: v for k, v in os.environ.items() if k not in (
|
||||
'SWAYSOCK', 'I3SOCK', 'WAYLAND_DISPLAY', 'DISPLAY', 'XDG_RUNTIME_DIR',
|
||||
'LD_LIBRARY_PATH', 'LD_PRELOAD', 'PKG_CONFIG_PATH', 'OPT_TRACE')}
|
||||
env['PYTHONDONTWRITEBYTECODE'] = '1'
|
||||
commands = []
|
||||
|
||||
def run(args, cwd=None, extra=None):
|
||||
i = len(commands)
|
||||
entry = {'args': list(map(str, args)), 'cwd': str(cwd or repo), 'env_override': extra or {}}
|
||||
commands.append(entry)
|
||||
with (raw/f'build-{i:03}.log').open('wb') as out:
|
||||
proc = subprocess.Popen(entry['args'], cwd=cwd or repo, env={**env, **(extra or {})},
|
||||
stdout=out, stderr=subprocess.STDOUT, start_new_session=True)
|
||||
try:
|
||||
proc.wait(timeout=600)
|
||||
except subprocess.TimeoutExpired:
|
||||
# Own only this command's group; compiler children must not outlive
|
||||
# a failed reproduction. No process-name or desktop-wide cleanup.
|
||||
os.killpg(proc.pid, signal.SIGTERM)
|
||||
try:
|
||||
proc.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
os.killpg(proc.pid, signal.SIGKILL)
|
||||
proc.wait()
|
||||
try:
|
||||
os.killpg(proc.pid, signal.SIGKILL)
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
entry['timed_out'] = True
|
||||
entry['returncode'] = proc.returncode
|
||||
(results/'build-commands.json').write_text(json.dumps(commands, indent=2)+'\n')
|
||||
if proc.returncode:
|
||||
raise RuntimeError(f'command {i} failed; inspect {raw}/build-{i:03}.log')
|
||||
|
||||
def archive(git, rev, name):
|
||||
target = lab/name
|
||||
target.mkdir()
|
||||
arc = lab/(name+'.tar')
|
||||
with arc.open('wb') as out:
|
||||
subprocess.run(['git', '-C', str(git), 'archive', rev], stdout=out, check=True, env=env)
|
||||
with tarfile.open(arc) as tf:
|
||||
tf.extractall(target, filter='data')
|
||||
return target
|
||||
|
||||
pins = {'SwayFX': 'fd71a6bdc061bd633b488ae7b83e8a6981d22f86',
|
||||
'Mosaic': 'cd550382b311efdbfe3ed892f4e1d281d852e754',
|
||||
'SceneFX': '37ccd723bef49e6891156ffafce8f549f01446cc',
|
||||
'wlroots': 'd783533489e1f75d6886c2ab5c5960090ef268f8'}
|
||||
stock = archive(repo, pins['SwayFX'], 'stock')
|
||||
archive(a.scenefx_git, pins['SceneFX'], 'scene-pristine')
|
||||
archive(a.scenefx_git, pins['SceneFX'], 'scene-private')
|
||||
archive(a.wlroots_git, pins['wlroots'], 'wlroots-source')
|
||||
archive(repo, pins['Mosaic'], 'mosaic-private')
|
||||
patchdir = lab/'patches'; patchdir.mkdir()
|
||||
patch = patchdir/'production.patch'; shutil.copyfile(repo/'patches/0001-mosaic-layout-viewport.patch', patch)
|
||||
run(['git', 'apply', '--check', patch], stock); run(['git', 'apply', patch], stock)
|
||||
for rel in ('include', 'sway', 'meson.build', 'meson_options.txt', 'tests/mosaic-model.c'):
|
||||
run(['git', 'diff', '--no-index', '--exit-code', stock/rel, repo/rel])
|
||||
chains = {'scene-private': ['step20c-valid-grab/scenefx-popup-plus-base.patch',
|
||||
'step20c-temporal-damage/scenefx-timing.patch',
|
||||
'step20c-optimized-blur-cache/scenefx-cache-trace.patch'],
|
||||
'mosaic-private': ['step20c-valid-grab/mosaic-popup-plus-base.patch',
|
||||
'step20c-valid-grab/hidden-guard.patch',
|
||||
'step20c-temporal-damage/mosaic-output-timing.patch',
|
||||
'step20c-presentation-identity/presentation-events.patch',
|
||||
'step20c-optimized-blur-cache/sway-source-trace.patch']}
|
||||
for tree, chain in chains.items():
|
||||
for rel in chain:
|
||||
dst = patchdir/Path(rel).name; shutil.copyfile(repo/'tooling/probes'/rel, dst)
|
||||
run(['git', 'apply', '--check', dst], lab/tree); run(['git', 'apply', dst], lab/tree)
|
||||
flags = ['-Dwerror=false', '-Dsd-bus-provider=libsystemd', '-Dmosaic-package=true',
|
||||
'-Dswaybar=false', '-Dswaynag=false', '-Dman-pages=disabled', '-Ddefault-wallpaper=false']
|
||||
run(['meson', 'setup', lab/'stock-build', stock, '--prefix=/usr', '--bindir=bin', *flags])
|
||||
run(['meson', 'compile', '-C', lab/'stock-build', '-j', '8'])
|
||||
run(['meson', 'test', '-C', lab/'stock-build', 'mosaic-model', '--print-errorlogs'])
|
||||
run(['meson', 'install', '-C', lab/'stock-build', '--destdir', lab/'stage-current', '--no-rebuild'])
|
||||
for name in ('pristine', 'private'):
|
||||
run(['meson', 'setup', lab/('scene-'+name+'-build'), lab/('scene-'+name), '-Dexamples=false', '-Dwerror=false'])
|
||||
run(['meson', 'compile', '-C', lab/('scene-'+name+'-build'), '-j', '8'])
|
||||
private_env = {'PKG_CONFIG_PATH': str(lab/'scene-private-build/meson-uninstalled')}
|
||||
run(['meson', 'setup', lab/'private-build', lab/'mosaic-private', *flags], extra=private_env)
|
||||
run(['meson', 'compile', '-C', lab/'private-build', '-j', '8'], extra=private_env)
|
||||
run(['meson', 'test', '-C', lab/'private-build', 'mosaic-model', '--print-errorlogs'])
|
||||
if a.static:
|
||||
# Source inspection establishes a native Meson library() target honoring this
|
||||
# option. Only SceneFX is embedded; wlroots and other dependencies stay shared.
|
||||
run(['meson', 'setup', lab/'scene-static-build', lab/'scene-private',
|
||||
'-Ddefault_library=static', '-Dexamples=false', '-Dwerror=false'])
|
||||
run(['meson', 'compile', '-C', lab/'scene-static-build', '-j', '8'])
|
||||
static_env = {'PKG_CONFIG_PATH': str(lab/'scene-static-build/meson-uninstalled')}
|
||||
run(['meson', 'setup', lab/'static-build', lab/'mosaic-private', *flags], extra=static_env)
|
||||
run(['meson', 'compile', '-C', lab/'static-build', '-j', '8'], extra=static_env)
|
||||
run(['meson', 'test', '-C', lab/'static-build', 'mosaic-model', '--print-errorlogs'])
|
||||
if a.pristine_binary:
|
||||
# Retained pristine binary is tied to the sealed A campaign identity.
|
||||
assert hashlib.sha256(a.pristine_binary.read_bytes()).hexdigest() == '8ac865481bc0487344210cd0e16d8e6c7655b29db887cd58ddc11b0ca22fe494'
|
||||
# Copy/hash only, never launch it.
|
||||
shutil.copyfile(a.pristine_binary, lab/'retained-pristine-sway')
|
||||
pins['pristine_binary_origin'] = str(a.pristine_binary.resolve())
|
||||
pins['pristine_binary_sha256'] = hashlib.sha256(a.pristine_binary.read_bytes()).hexdigest()
|
||||
(results/'source-pins.json').write_text(json.dumps(pins, indent=2)+'\n')
|
||||
print('PASS private builds/model tests and single stock staging; zero compositor launches')
|
||||
36
tooling/probes/step20c-scenefx-abi-package/seal.py
Normal file
36
tooling/probes/step20c-scenefx-abi-package/seal.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Seal completed source/evidence inventories once, or verify existing bytes.
|
||||
|
||||
WHY: derived reports cannot silently overwrite the evidence they cite.
|
||||
CONTRACT: fixtures finalized first; report/diff are captured outside inventory.
|
||||
INVARIANT: no manifest replacement, no binaries/build trees in source inventory.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
|
||||
p=argparse.ArgumentParser(description=__doc__);p.add_argument('lab',type=Path)
|
||||
p.add_argument('--verify',action='store_true');a=p.parse_args();lab=a.lab.resolve()
|
||||
fixture=Path(__file__).resolve().parent;repo=fixture.parents[2]
|
||||
assert lab.is_relative_to(Path.home()) and lab!=Path.home() and not lab.is_relative_to(repo)
|
||||
r=lab/'results'
|
||||
def sha(path):return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
if not a.verify:
|
||||
assert not any((r/(name+'-sha256.txt')).exists() for name in ['source','evidence'])
|
||||
source=set(p for p in fixture.iterdir() if p.is_file())
|
||||
for tree in ['stock','scene-pristine','scene-private','wlroots-source','mosaic-private']:
|
||||
source.update(p for p in (lab/tree).rglob('*') if p.is_file() and 'subprojects' not in p.relative_to(lab/tree).parts)
|
||||
source.update(p for p in (lab/'patches').iterdir() if p.is_file())
|
||||
source.update(p for p in lab.iterdir() if p.is_file() and p.suffix in ['.py','.c','.md'])
|
||||
evidence=set(p for p in (lab/'raw').iterdir() if p.is_file())
|
||||
evidence.update(p for p in r.iterdir() if p.is_file() and not p.name.endswith('-sha256.txt'))
|
||||
for name,files in [('source',source),('evidence',evidence)]:
|
||||
manifest=r/(name+'-sha256.txt')
|
||||
# Exclusive creation, never truncate a prior result on interrupted runs.
|
||||
with manifest.open('x') as out:
|
||||
for path in sorted(files):out.write(f'{sha(path)} {path}\n')
|
||||
for name in ['source','evidence']:
|
||||
path=r/(name+'-sha256.txt');lines=path.read_text().splitlines()
|
||||
for line in lines:
|
||||
expected,filename=line.split(' ',1);assert sha(Path(filename))==expected,filename
|
||||
print(name,len(lines),sha(path),'PASS')
|
||||
77
tooling/probes/step20c-scenefx-abi-package/summarize.py
Normal file
77
tooling/probes/step20c-scenefx-abi-package/summarize.py
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Summarize measured evidence without turning a candidate into an approval.
|
||||
|
||||
CONTRACT: successful S0 and expected incompatible naive S1 are distinct.
|
||||
INVARIANT: no ABI inference from SONAME, compile success, or struct size alone.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
p=argparse.ArgumentParser(description=__doc__);p.add_argument('lab',type=Path)
|
||||
a=p.parse_args();lab=a.lab.resolve();r=lab/'results'
|
||||
assert not (r/'evidence-sha256.txt').exists(), 'already sealed'
|
||||
def read(name):return json.loads((r/(name+'.json')).read_text())
|
||||
def save(name,value):(r/(name+'.json')).write_text(json.dumps(value,indent=2)+'\n')
|
||||
assert read('stock-stage-manifest')['status']=='CURRENT_PACKAGE_STAGE_PASS'
|
||||
assert read('unresolved-symbols')['classification']=='INCOMPATIBLE_WITH_STOCK_SCENEFX'
|
||||
options={
|
||||
'A_upstream':{'technical':'UNKNOWN acceptance; API source prototype demonstrated',
|
||||
'system_library_intact':'normal maintained upstream upgrade required; no private overwrite',
|
||||
'sway_intact':True,'coexistence':'conditional on supported upstream API/version',
|
||||
'stable_API':'NOT AVAILABLE in pinned stock','ABI':'NOT ESTABLISHED for future upstream release',
|
||||
'one_file':'possible after adoption, NOT TESTED','upgrade_risk':'medium; shared dependency transition',
|
||||
'SceneFX_maintenance':'upstream if accepted; no commitment obtained','wlroots':'version-paired qualification',
|
||||
'reproducibility':'private prototype only','human_decision':True,
|
||||
'remaining':['accepted ownership/error/bounds contract','upstream maintainer agreement','public ABI/version policy','graphical feasibility gates']},
|
||||
'B_static':{'technical':'shared-source static build and loader PASS',
|
||||
'system_library_intact':True,'sway_intact':True,'coexistence':'conditional; no filesystem overlap',
|
||||
'stable_API':'private','ABI':'17-struct layout and current relocations PASS; future safety NOT ESTABLISHED',
|
||||
'one_file':'binary dependency shape demonstrated, static stage NOT TESTED',
|
||||
'upgrade_risk':'high until explicit wlroots interposition/update policy',
|
||||
'SceneFX_maintenance':'Mosaic must own all embedded security fixes; no owner committed',
|
||||
'wlroots':'still shared libwlroots-0.20.so; overlapping exports must be qualified',
|
||||
'reproducibility':'fixed sources/patches/commands retained; bit reproducibility NOT TESTED',
|
||||
'human_decision':True,'remaining':['symbol binding contract','wlroots version matrix','MIT notices in one-file distribution','security/rebase owner','static package contract acceptance','graphical tests separately authorized']},
|
||||
'C_private_shared':{'technical':'same-SONAME private process resolution PASS; distinct-SONAME candidate NOT TESTED',
|
||||
'system_library_intact':True,'sway_intact':True,'coexistence':'conditional on separate file/SONAME and complete binding audit',
|
||||
'stable_API':'private','ABI':'future isolated distribution NOT ESTABLISHED',
|
||||
'one_file':False,'upgrade_risk':'medium/high; shared wlroots still imposes constraints',
|
||||
'SceneFX_maintenance':'Mosaic-private library owner required','wlroots':'shared consumers/interposition remain',
|
||||
'reproducibility':'laboratory lookup only','human_decision':True,
|
||||
'remaining':['new owned library file and packaging decision','distinct SONAME/DT_NEEDED','private RUNPATH/transitive lookup','no stock/private duplication within process','security owner']},
|
||||
'D_no_new_API':{'technical':'UNKNOWN; no implementation authorized',
|
||||
'system_library_intact':True,'sway_intact':True,'coexistence':'conditional on valid compositor architecture',
|
||||
'stable_API':'existing unstable scene API only','ABI':'new dependency avoided, architecture unproved',
|
||||
'one_file':'possible, NOT TESTED','upgrade_risk':'unknown, broad renderer maintenance possible',
|
||||
'SceneFX_maintenance':'normal upstream for stock; Mosaic owns alternate mechanism',
|
||||
'wlroots':'same version-paired graphical contracts','reproducibility':'NOT TESTED','human_decision':True,
|
||||
'remaining':['popup/global stacking','hit traversal','damage/visibility','effects and saved buffers','rotation/scaling/migration','transaction state consistency']}}
|
||||
save('architectural-options',options)
|
||||
save('coexistence-matrix',{'S0':'no filesystem conflict; stock dynamic dependency',
|
||||
'S1_naive_stock':'missing two symbols; cannot be distributed as stock-compatible',
|
||||
'S2_static':'no SceneFX file, 85 overlapping exports with shared wlroots',
|
||||
'S2_shared':'extra private library file requires new package ownership contract',
|
||||
'standard_sway':'no modification in any experiment'})
|
||||
save('upgrade-rollback-risks',{'current_upgrade':'no conflicts/provides/replaces prevents package-manager block; shared SONAME/API changes can still break Mosaic and require separate rebuild',
|
||||
'static_upgrade':'SceneFX system upgrade does not update embedded code; shared wlroots remains an ABI constraint',
|
||||
'private_shared_upgrade':'private file protects library ownership; shared wlroots plus symbol semantics still require qualification',
|
||||
'rollback':'hypothetical remove only /usr/bin/swayfx-mosaic in S0; stock sway/libraries untouched; actual removal NOT TESTED',
|
||||
'forbidden':'pinning shared system dependencies to block stock SwayFX updates'})
|
||||
save('decision-gates',{'public_API':'new decision before implementation',
|
||||
'package_contract':'unchanged; static bundling or extra private library requires explicit decision',
|
||||
'GPU_lifetime_and_cache':'NOT TESTED here; separate from linker evidence',
|
||||
'recommended_next':'bounded non-graphical design of symbol/ownership/update and license contracts for static candidate; prepare upstream API dossier without submission',
|
||||
'integration_authorized':False})
|
||||
save('matrix',{'S0_build':'PASS','S0_model':'PASS','S0_stage':'CURRENT_PACKAGE_STAGE_PASS',
|
||||
'S1_private_loader':'PASS relocations only','S1_stock_loader':'INCOMPATIBLE_WITH_STOCK_SCENEFX',
|
||||
'S2_static_build':'PASS','S2_static_loader':'PASS relocations only','distinct_SONAME_build':'NOT TESTED',
|
||||
'graphics':'NOT RUN / FORBIDDEN','system_install':'NOT RUN / FORBIDDEN','bit_reproducibility':'NOT TESTED'})
|
||||
save('summary',{'verdict':'PARTIALLY_FEASIBLE','architecture':'SCENEFX_ABI_PACKAGE_REQUIRES_ARCHITECTURAL_DECISION',
|
||||
'current_package':'CURRENT_PACKAGE_STAGE_PASS','experimental_stock':'INCOMPATIBLE_WITH_STOCK_SCENEFX',
|
||||
'candidate':'static embedding demonstrated technically; unresolved maintenance, symbol and license gates',
|
||||
'long_term_direction':'upstream maintained API preferred conditionally, no acceptance presumed',
|
||||
'budget':{'stock_rebuild':1,'stock_stage':1,'private_shared_chain':1,'optional_static_chain':1,
|
||||
'pristine_SceneFX_reference_build':1,'compositor_launches':0,'graphical_runs':0,'installs':0},
|
||||
'phase_B_commit_push':False})
|
||||
print(json.dumps(read('summary'),indent=2))
|
||||
Loading…
Reference in a new issue