feat(xpkg): exports schema + predicate-driven elfpatch#9
Merged
Sunrisepeak merged 2 commits intomainfrom May 2, 2026
Merged
Conversation
Add `xpm.<platform>.exports.runtime.{loader, libdirs, abi}` schema field,
parsed by xpkg-loader and surfaced on PlatformMatrix as a per-platform
ExportsBlock. Schema is the only new metadata field; all sub-fields
optional (providers only declare non-default behavior).
Extend xpkg-executor's _RUNTIME injection with:
* runtime_deps_list / build_deps_list — split form (post-#249)
* deps_exports — pre-resolved per-runtime-dep
{ loader, libdirs, abi }
* self_exports — same shape, for the current pkg
Rewrite elfpatch.lua's auto path to be predicate-driven:
1. user_skip → return
2. user_override → use elfpatch.set({...}) opts
3. self_exports → patch with own loader (handles glibc self-patch)
4. runtime deps → unique loader-provider wins; ≥2 require interp_from
5. otherwise → no-op (consumer uses host libc / no ELF / static)
New API: M.set(opts) and M.skip(); old M.auto kept as deprecation alias
mapped onto M.set with debug log (silent in normal mode, only verbose
users see). M.apply_auto retained as a back-compat name for xlings's
apply_elfpatch_auto bridge — internally calls the new M._apply.
Also adds e_machine cross-arch protection and a static-binary skip path
in _collect_targets / _has_pt_interp helpers.
Tests: 11/11 LoaderTest pass (incl. 3 new fixtures for full / loader-only /
no-exports parsing edge cases).
This was referenced May 2, 2026
Merged
Previous deprecation alias mapped legacy elfpatch.auto({...}) onto the
new elfpatch.set({...}) API, which broke pre-existing consumers that
relied on auto({enable=true}) defaulting to loader='subos' (system
detection). After the remap, set({}) with no interpreter became a
no-op in _apply, breaking 3 ExecutorTest cases.
Fix: legacy auto() now sets _RUNTIME.elfpatch_legacy_* flags only.
M.apply_auto routes between three paths:
1. user_skip → return
2. user_override (set) → new predicate-aware path
3. legacy_auto (auto) → old loader='subos' default path
4. neither → predicate-driven default
Both old and new APIs coexist for the half-year transition window
without one mangling the other's semantics. ExecutorTest 22/22 pass
again.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
xpm.<platform>.exports.runtime.{loader, libdirs, abi}schema field and rewriteselfpatch.luato be predicate-driven post-install instead of relying on consumers to callelfpatch.auto({...})in install hooks.Detailed design + rationale: openxlings/xlings docs/plans/2026-05-02-elfpatch-exports-design.md (incoming PR).
Schema additions
All sub-fields optional. Providers only declare non-default behavior.
elfpatch.lua
New consumer-facing API:
elfpatch.set(opts)— override predicate-driven auto with explicit paramselfpatch.skip()— opt out of auto-patch entirelyOld
elfpatch.auto({...})retained as deprecation alias mapping toelfpatch.set({...}). Drop after 2026-11. Debug-level deprecation log on first call._apply()decision tree:user_skip→ returnuser_override→ use hook-given optsself_exports.loaderset → patch with own loader (glibc self-patch)exports.loader→ use itexports.loader→ fail-fast, requireinterp_fromPlus:
e_machinecross-arch protection, static-binary skip,runtime_deps_list(not union) for RPATH closure → fixes 0.0.32 build_dep pollution._RUNTIME extensions
runtime_deps_list/build_deps_list(split, post-#249)deps_exportslookup{ [dep_spec] = { loader, libdirs, abi } }self_exportsTest plan
Cross-repo
This PR alone is non-breaking — no consumer reads the new field yet, deprecation alias preserves old hook API. Real consumer impact lands after:
add_requires(drafted)