AtlatestRepositorycore-channel

core-channel / tree / src / core / packagesluajit.sgl

1;;; LuaJIT.
2;;;
3;;; PROVENANCE: READ THIS BEFORE PUBLISHING OR SIGNING ANYTHING HERE.
4;;;
5;;; This package is pinned to a GIT COMMIT fetched as a GitHub auto-generated
6;;; archive, because LuaJIT PUBLISHES NO RELEASE TARBALL OF ANY KIND -- no
7;;; release assets, and luajit.org's download page links only to git
8;;; repositories. Its last tag is v2.1.0-beta3 from 2017; every distribution
9;;; pins a commit.
10;;;
11;;; That is a WEAKER provenance than anything else in this channel, and it is a
12;;; deliberate, scoped exception rather than a precedent. The rule it appears
13;;; to break -- that a synthetic archive whose bytes can be regenerated does
14;;; not belong on a critical path -- was set for a chain INTENDED TO SHIP.
15;;; This package and `core/swayimg` exist for a different reason: they are a
16;;; DISCOVERY EXERCISE whose product is knowledge about what this packaging
17;;; model can and cannot host. Two auto-generated archives are acceptable for
18;;; that and would not be acceptable for a shipped chain.
19;;;
20;;; SO: NEITHER THIS PACKAGE NOR `core/swayimg` SHOULD BE PUBLISHED OR SIGNED
21;;; ON THIS PROVENANCE WITHOUT REVISITING THE QUESTION. If LuaJIT is ever
22;;; wanted for real, the question to answer first is what artifact to pin, not
23;;; how to build it -- building it is solved, below.
24(define-library (core packages luajit)
25 (import (sigil core)
26 (core build-environments)
27 (sigil env package)
28 (sigil build external))
29 (export luajit)
30 (begin
31 ;; THE ONE THING THAT STOPPED IT BUILDING, and it is a property of the
32 ;; CHANNEL rather than of LuaJIT.
33 ;;
34 ;; Everything that looks hard about building a JIT under a foreign
35 ;; toolchain worked first time: minilua bootstrapped, DynASM generated
36 ;; x86-64 assembly, clang's integrated assembler consumed it, and
37 ;; `libluajit.a` linked. Only the FINAL EXECUTABLE link failed:
38 ;;
39 ;; ld.lld: error: undefined symbol: _Unwind_SetIP
40 ;; ld.lld: error: undefined symbol: _Unwind_GetIP
41 ;; ld.lld: error: undefined symbol: _Unwind_RaiseException
42 ;; ld.lld: error: undefined symbol: __register_frame
43 ;; ld.lld: error: undefined symbol: __deregister_frame
44 ;;
45 ;; The builder's LDFLAGS ships no DWARF unwinder. The toolchain HAS one
46 ;; (zig's `lib/libunwind`); it is simply not linked by default. All five
47 ;; symbols were confirmed present in `-lunwind` INDIVIDUALLY -- the last
48 ;; two are a different family, the GCC JIT frame-registration entry points,
49 ;; and a control on only the first would not have covered them.
50 ;; [[topics/core-channel-links-no-dwarf-unwinder]]
51 ;;
52 ;; `TARGET_XLIBS` is EXTENDED, not replaced: its default is `-lm`, and
53 ;; dropping that would break the link for a different reason.
54 (define luajit
55 (package
56 name: "luajit"
57 version: "2.1.1787165859"
58 source: (external-source
59 location: "https://codeload.github.com/LuaJIT/LuaJIT/tar.gz/1ee778a4e37122d8ca7d5733c590a47dafd6b15c"
60 sha256: "512cdb23dafa39c6247763ae15f29341734b1596067b067db0c0387bbebc7c55")
61 buildenv: core-buildenv
62 phases: (list
63 ;; WHY `run` PHASES AND NOT `make`/`install` PHASES, which
64 ;; would be shorter.
65 ;;
66 ;; LuaJIT's src/Makefile:29 says `CC= $(DEFAULT_CC)`. A
67 ;; MAKEFILE assignment beats the ENVIRONMENT in make's
68 ;; precedence rules, so the builder's `CC` is silently
69 ;; ignored and LuaJIT compiles with its own default -- which
70 ;; is not the channel's target-explicit wrapper. Only a
71 ;; command-line assignment beats a makefile one.
72 ;;
73 ;; But a `make` phase's arguments are shell-quoted
74 ;; individually, so `CC=$CC` would be passed as that literal
75 ;; four-character string rather than expanded. And writing
76 ;; the compiler's NAME here would defeat the invariant that
77 ;; the compiler is DERIVED from the derivation's target --
78 ;; the same invariant `build-environment-assignments` refuses
79 ;; an `env: CC=` override to protect.
80 ;;
81 ;; A `run` phase with `sh -c` is the honest way out: the
82 ;; shell expands `$CC`, so this FORWARDS whatever the builder
83 ;; set without naming it. Note `$CC` and not `${CC}` --
84 ;; braces are intercepted by sigil's own interpolation before
85 ;; any shell sees them.
86 (external-phase
87 kind: 'run
88 args: (list "sh" "-c"
89 (string-append
90 "set -eu\n"
91 "echo \"LUAJIT-FORWARDED-CC=[$CC]\"\n"
92 "make -j\"$(nproc 2>/dev/null || echo 4)\""
93 " CC=\"$CC\" HOST_CC=\"$CC\""
94 " BUILDMODE=static"
95 " TARGET_XLIBS=\"-lm -lunwind\""
96 ;; `TARGET_STRIP=copy-strip` BECAUSE THE SEED HAS
97 ;; NO `strip`. LuaJIT's link rule ends with
98 ;; `$(TARGET_STRIP) $@`, which dies:
99 ;;
100 ;; make[1]: strip: No such file or directory
101 ;; make[1]: *** [Makefile:754: luajit] Error 127
102 ;;
103 ;; Another instance of the family this channel
104 ;; keeps meeting -- `yacc`, `file`, `gperf`,
105 ;; `python3` -- except this one fails LOUDLY with
106 ;; 127 rather than producing a confident wrong
107 ;; answer, which is the better direction.
108 ;;
109 ;; Nothing needs to be packaged for it: the
110 ;; binary is ALREADY stripped, because the
111 ;; builder's LDFLAGS carries `-s`. A second pass
112 ;; would be redundant even if the tool existed.
113 ;; `copy-strip` is the toolchain's own no-op
114 ;; stand-in for exactly this shape
115 ;; (toolchain/setup.sh writes it); invoked with a
116 ;; single argument and no `-o`, it validates the
117 ;; input and does nothing.
118 " TARGET_STRIP=copy-strip"
119 "\n"
120 "echo LUAJIT-BUILT\n")))
121 ;; `install` re-enters make, whose INSTALL_DEP is
122 ;; `src/luajit` -- so it must be invoked with the same CC or
123 ;; a stale-dependency check could relink with the wrong
124 ;; compiler. Same forwarding, same reason.
125 ;;
126 ;; LuaJIT wants `PREFIX` (uppercase); the builder's `install`
127 ;; phase supplies lowercase `prefix`, which LuaJIT ignores.
128 ;; It does honour `DESTDIR`, which is derived here from the
129 ;; phase's cwd as everywhere else in this channel.
130 (external-phase
131 kind: 'run
132 args: (list "sh" "-c"
133 (string-append
134 "set -eu\n"
135 "d=\"$(dirname \"$PWD\")/install-root\"\n"
136 "make install"
137 " CC=\"$CC\" HOST_CC=\"$CC\""
138 " BUILDMODE=static"
139 " TARGET_XLIBS=\"-lm -lunwind\""
140 " TARGET_STRIP=copy-strip"
141 " PREFIX=\"${out}\" DESTDIR=\"$d\"\n"
142 "echo LUAJIT-INSTALLED\n")))
143 ;; ASSERT THE ARTIFACT BEHAVIOURALLY, not by existence.
144 ;;
145 ;; `test -x bin/luajit` would pass on a binary that cannot
146 ;; run. What is actually in question for an
147 ;; assembly-generating build under a foreign toolchain is
148 ;; whether the JIT WORKS -- so run a loop big enough to be
149 ;; compiled and check the JIT is still enabled afterwards.
150 ;; `jit ~= nil` alone only proves the module exists.
151 (external-phase
152 kind: 'run
153 args: (list "sh" "-c"
154 (string-append
155 "set -eu\n"
156 "d=\"$(dirname \"$PWD\")/install-root\"\n"
157 "lj=\"$d${out}/bin/luajit\"\n"
158 "test -x \"$lj\"\n"
159 "v=$(\"$lj\" -e 'io.write(6*7)')\n"
160 "echo \"LUAJIT-ARITHMETIC=[$v]\"\n"
161 "test \"$v\" = 42\n"
162 "s=$(\"$lj\" -e 'local n=0 for i=1,2e6 do n=n+i end io.write(n,\"|\",tostring(jit.status()))')\n"
163 "echo \"LUAJIT-HOTLOOP=[$s]\"\n"
164 "test \"$s\" = '2000001000000|true'\n"
165 ;; The `.pc` matters: swayimg finds this through
166 ;; `dependency('luajit')`, and it must land in
167 ;; lib/pkgconfig because this channel composes
168 ;; PKG_CONFIG_PATH from exactly that directory.
169 "test -f \"$d${out}/lib/pkgconfig/luajit.pc\"\n"
170 "echo LUAJIT-VERIFIED\n"))))))))