Add LuaJIT, and record why its provenance is a scoped exception
LuaJIT builds under this toolchain. Everything that looks hard about that worked first time -- minilua bootstrapped, DynASM generated x86-64 assembly, clang's integrated assembler consumed it, libluajit.a linked. Two things stopped the final executable:
- The builder's LDFLAGS ships no DWARF unwinder, so lj_err.o's five Unwind*/__register_frame references were undefined at the last step. The toolchain HAS one; TARGET_XLIBS is extended (not replaced -- its default -lm matters) with -lunwind. All five symbols were confirmed present individually, because the last two are a different family and a control on the first would not have covered them.
stripis not in the seed, and LuaJIT's link rule ends with $(TARGET_STRIP). Nothing needed packaging: the builder's -s already strips at link, and the toolchain's copy-strip is a no-op stand-in.
It is a run-phase package rather than make/install phases for a reason worth knowing. LuaJIT's src/Makefile assigns CC= $(DEFAULT_CC), and a makefile assignment beats the environment in make's precedence, so the builder's CC is silently ignored. Only a command-line assignment wins -- but a make phase's arguments are shell-quoted individually, so CC=$CC would be passed as that literal string, and naming the compiler here would defeat the invariant that it is derived from the derivation's target. sh -c lets the shell expand $CC, which forwards whatever the builder set without naming it.
Verified behaviourally rather than by existence: the installed binary computes 6*7, and a two-million-iteration loop returns the right sum with jit.status() still true afterwards -- which is the difference between "a Lua interpreter linked" and "the JIT works".
PROVENANCE. LuaJIT publishes no release tarball of any kind; this pins a git commit fetched as a GitHub auto-generated archive. That is weaker than anything else in this channel and is a deliberate, scoped exception: the rule it appears to break was set for a chain intended to ship, and this is a discovery exercise whose product is knowledge about what the packaging model can host. Neither this nor core/swayimg should be published or signed on this provenance without revisiting the question. Said at the package site too.
channel.sgl | 6 ++++--
src/core/packages/luajit.sgl | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 174 insertions(+), 2 deletions(-)channel.sglmodified
(core packages datastructures) (core packages wayland) (core packages input) (core packages capture)) (core packages capture) (core packages luajit))(channel stream: "core-stable" (channel-binding name: "core/foot" value: foot) (channel-binding name: "core/vim" value: vim) (channel-binding name: "core/emacs" value: emacs) (channel-binding name: "core/python" value: python))) (channel-binding name: "core/python" value: python) (channel-binding name: "core/luajit" value: luajit)))src/core/packages/luajit.sgladded
;;; LuaJIT.;;;;;; PROVENANCE: READ THIS BEFORE PUBLISHING OR SIGNING ANYTHING HERE.;;;;;; This package is pinned to a GIT COMMIT fetched as a GitHub auto-generated;;; archive, because LuaJIT PUBLISHES NO RELEASE TARBALL OF ANY KIND -- no;;; release assets, and luajit.org's download page links only to git;;; repositories. Its last tag is v2.1.0-beta3 from 2017; every distribution;;; pins a commit.;;;;;; That is a WEAKER provenance than anything else in this channel, and it is a;;; deliberate, scoped exception rather than a precedent. The rule it appears;;; to break -- that a synthetic archive whose bytes can be regenerated does;;; not belong on a critical path -- was set for a chain INTENDED TO SHIP.;;; This package and `core/swayimg` exist for a different reason: they are a;;; DISCOVERY EXERCISE whose product is knowledge about what this packaging;;; model can and cannot host. Two auto-generated archives are acceptable for;;; that and would not be acceptable for a shipped chain.;;;;;; SO: NEITHER THIS PACKAGE NOR `core/swayimg` SHOULD BE PUBLISHED OR SIGNED;;; ON THIS PROVENANCE WITHOUT REVISITING THE QUESTION. If LuaJIT is ever;;; wanted for real, the question to answer first is what artifact to pin, not;;; how to build it -- building it is solved, below.(define-library (core packages luajit) (import (sigil core) (core build-environments) (sigil env package) (sigil build external)) (export luajit) (begin ;; THE ONE THING THAT STOPPED IT BUILDING, and it is a property of the ;; CHANNEL rather than of LuaJIT. ;; ;; Everything that looks hard about building a JIT under a foreign ;; toolchain worked first time: minilua bootstrapped, DynASM generated ;; x86-64 assembly, clang's integrated assembler consumed it, and ;; `libluajit.a` linked. Only the FINAL EXECUTABLE link failed: ;; ;; ld.lld: error: undefined symbol: _Unwind_SetIP ;; ld.lld: error: undefined symbol: _Unwind_GetIP ;; ld.lld: error: undefined symbol: _Unwind_RaiseException ;; ld.lld: error: undefined symbol: __register_frame ;; ld.lld: error: undefined symbol: __deregister_frame ;; ;; The builder's LDFLAGS ships no DWARF unwinder. The toolchain HAS one ;; (zig's `lib/libunwind`); it is simply not linked by default. All five ;; symbols were confirmed present in `-lunwind` INDIVIDUALLY -- the last ;; two are a different family, the GCC JIT frame-registration entry points, ;; and a control on only the first would not have covered them. ;; [[topics/core-channel-links-no-dwarf-unwinder]] ;; ;; `TARGET_XLIBS` is EXTENDED, not replaced: its default is `-lm`, and ;; dropping that would break the link for a different reason. (define luajit (package name: "luajit" version: "2.1.1787165859" source: (external-source location: "https://codeload.github.com/LuaJIT/LuaJIT/tar.gz/1ee778a4e37122d8ca7d5733c590a47dafd6b15c" sha256: "512cdb23dafa39c6247763ae15f29341734b1596067b067db0c0387bbebc7c55") buildenv: core-buildenv phases: (list ;; WHY `run` PHASES AND NOT `make`/`install` PHASES, which ;; would be shorter. ;; ;; LuaJIT's src/Makefile:29 says `CC= $(DEFAULT_CC)`. A ;; MAKEFILE assignment beats the ENVIRONMENT in make's ;; precedence rules, so the builder's `CC` is silently ;; ignored and LuaJIT compiles with its own default -- which ;; is not the channel's target-explicit wrapper. Only a ;; command-line assignment beats a makefile one. ;; ;; But a `make` phase's arguments are shell-quoted ;; individually, so `CC=$CC` would be passed as that literal ;; four-character string rather than expanded. And writing ;; the compiler's NAME here would defeat the invariant that ;; the compiler is DERIVED from the derivation's target -- ;; the same invariant `build-environment-assignments` refuses ;; an `env: CC=` override to protect. ;; ;; A `run` phase with `sh -c` is the honest way out: the ;; shell expands `$CC`, so this FORWARDS whatever the builder ;; set without naming it. Note `$CC` and not `${CC}` -- ;; braces are intercepted by sigil's own interpolation before ;; any shell sees them. (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "echo \"LUAJIT-FORWARDED-CC=[$CC]\"\n" "make -j\"$(nproc 2>/dev/null || echo 4)\"" " CC=\"$CC\" HOST_CC=\"$CC\"" " BUILDMODE=static" " TARGET_XLIBS=\"-lm -lunwind\"" ;; `TARGET_STRIP=copy-strip` BECAUSE THE SEED HAS ;; NO `strip`. LuaJIT's link rule ends with ;; `$(TARGET_STRIP) $@`, which dies: ;; ;; make[1]: strip: No such file or directory ;; make[1]: *** [Makefile:754: luajit] Error 127 ;; ;; Another instance of the family this channel ;; keeps meeting -- `yacc`, `file`, `gperf`, ;; `python3` -- except this one fails LOUDLY with ;; 127 rather than producing a confident wrong ;; answer, which is the better direction. ;; ;; Nothing needs to be packaged for it: the ;; binary is ALREADY stripped, because the ;; builder's LDFLAGS carries `-s`. A second pass ;; would be redundant even if the tool existed. ;; `copy-strip` is the toolchain's own no-op ;; stand-in for exactly this shape ;; (toolchain/setup.sh writes it); invoked with a ;; single argument and no `-o`, it validates the ;; input and does nothing. " TARGET_STRIP=copy-strip" "\n" "echo LUAJIT-BUILT\n"))) ;; `install` re-enters make, whose INSTALL_DEP is ;; `src/luajit` -- so it must be invoked with the same CC or ;; a stale-dependency check could relink with the wrong ;; compiler. Same forwarding, same reason. ;; ;; LuaJIT wants `PREFIX` (uppercase); the builder's `install` ;; phase supplies lowercase `prefix`, which LuaJIT ignores. ;; It does honour `DESTDIR`, which is derived here from the ;; phase's cwd as everywhere else in this channel. (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "make install" " CC=\"$CC\" HOST_CC=\"$CC\"" " BUILDMODE=static" " TARGET_XLIBS=\"-lm -lunwind\"" " TARGET_STRIP=copy-strip" " PREFIX=\"${out}\" DESTDIR=\"$d\"\n" "echo LUAJIT-INSTALLED\n"))) ;; ASSERT THE ARTIFACT BEHAVIOURALLY, not by existence. ;; ;; `test -x bin/luajit` would pass on a binary that cannot ;; run. What is actually in question for an ;; assembly-generating build under a foreign toolchain is ;; whether the JIT WORKS -- so run a loop big enough to be ;; compiled and check the JIT is still enabled afterwards. ;; `jit ~= nil` alone only proves the module exists. (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "lj=\"$d${out}/bin/luajit\"\n" "test -x \"$lj\"\n" "v=$(\"$lj\" -e 'io.write(6*7)')\n" "echo \"LUAJIT-ARITHMETIC=[$v]\"\n" "test \"$v\" = 42\n" "s=$(\"$lj\" -e 'local n=0 for i=1,2e6 do n=n+i end io.write(n,\"|\",tostring(jit.status()))')\n" "echo \"LUAJIT-HOTLOOP=[$s]\"\n" "test \"$s\" = '2000001000000|true'\n" ;; The `.pc` matters: swayimg finds this through ;; `dependency('luajit')`, and it must land in ;; lib/pkgconfig because this channel composes ;; PKG_CONFIG_PATH from exactly that directory. "test -f \"$d${out}/lib/pkgconfig/luajit.pc\"\n" "echo LUAJIT-VERIFIED\n"))))))))