1{
2 lib,
3 stdenv,
4 cairo,
5 curl,
6 fetchurl,
7 freealut,
8 gdk-pixbuf,
9 git,
10 glib,
11 gnome2,
12 graphviz,
13 gtk2-x11,
14 interpreter,
15 libGL,
16 libGLU,
17 libogg,
18 librsvg,
19 libvorbis,
20 makeWrapper,
21 ncurses,
22 openal,
23 openssl,
24 pango,
25 pcre,
26 runCommand,
27 runtimeShell,
28 tzdata,
29 udis86,
30 unzip,
31 writeScriptBin,
32 zlib,
33}:
34let
35 runtimeLibs = [
36 cairo
37 freealut
38 gdk-pixbuf
39 glib
40 gnome2.gtkglext
41 graphviz
42 gtk2-x11
43 libGL
44 libGLU
45 libogg
46 libvorbis
47 openal
48 openssl
49 pango
50 pcre
51 udis86
52 zlib
53 ];
54
55 wrapFactorScript =
56 {
57 from,
58 to ? false,
59 runtimeLibs,
60 }:
61 ''
62 # Set Gdk pixbuf loaders file to the one from the build dependencies here
63 unset GDK_PIXBUF_MODULE_FILE
64 # Defined in gdk-pixbuf setup hook
65 findGdkPixbufLoaders "${librsvg}"
66
67 ${if (builtins.isString to) then "makeWrapper ${from} ${to}" else "wrapProgram ${from}"} \
68 --set GDK_PIXBUF_MODULE_FILE "$GDK_PIXBUF_MODULE_FILE" \
69 --argv0 factor \
70 --prefix LD_LIBRARY_PATH : /run/opengl-driver/lib:${lib.makeLibraryPath runtimeLibs} \
71 --prefix PATH : ${lib.makeBinPath [ graphviz ]}
72 '';
73
74 wrapFactor =
75 runtimeLibs:
76 runCommand (lib.appendToName "with-libs" interpreter).name
77 {
78 nativeBuildInputs = [ makeWrapper ];
79 buildInputs = [ gdk-pixbuf ];
80 passthru.runtimeLibs = runtimeLibs ++ interpreter.runtimeLibs;
81 }
82 (wrapFactorScript {
83 from = "${interpreter}/lib/factor/.factor-wrapped";
84 to = "$out/bin/factor";
85 runtimeLibs = (runtimeLibs ++ interpreter.runtimeLibs);
86 });
87
88 # Development helper for use in nix shell
89 wrapLocalFactor = writeScriptBin "wrapFactor" ''
90 #!${runtimeShell}
91 ${wrapFactorScript {
92 from = "./factor";
93 inherit runtimeLibs;
94 }}
95 ln -sf factor.image .factor-wrapped.image
96 '';
97 rev = "e10b64dbc53a8583098e73580a1eb9ff4ce0c709";
98 version = "0.99";
99
100in
101stdenv.mkDerivation {
102 pname = "factor-lang";
103 inherit version;
104
105 src = fetchurl {
106 url = "https://downloads.factorcode.org/releases/${version}/factor-src-${version}.zip";
107 sha256 = "f5626bb3119bd77de9ac3392fdbe188bffc26557fab3ea34f7ca21e372a8443e";
108 };
109
110 patches = [
111 ./staging-command-line-0.99-pre.patch
112 ./workdir-0.99-pre.patch
113 ./adjust-paths-in-unit-tests.patch
114 ];
115
116 nativeBuildInputs = [
117 git
118 makeWrapper
119 curl
120 unzip
121 wrapLocalFactor
122 ];
123 buildInputs = runtimeLibs;
124
125 postPatch = ''
126 sed -i -e '4i GIT_LABEL = heads/master-${rev}' GNUmakefile
127
128 # There is no ld.so.cache in NixOS so we patch out calls to that completely.
129 # This should work as long as no application code relies on `find-library*`
130 # to return a match, which currently is the case and also a justified assumption.
131
132 sed -i -e "s#/sbin/ldconfig -p#cat $out/lib/factor/ld.so.cache#g" \
133 basis/alien/libraries/finder/linux/linux.factor
134
135 # Some other hard-coded paths to fix:
136 sed -i 's#/usr/share/zoneinfo/#${tzdata}/share/zoneinfo/#g' \
137 extra/tzinfo/tzinfo.factor
138
139 sed -i 's#/usr/share/terminfo#${ncurses.out}/share/terminfo#g' \
140 extra/terminfo/terminfo.factor
141
142 # De-memoize xdg-* functions, otherwise they break the image.
143 sed -i -e 's/^MEMO:/:/' basis/xdg/xdg.factor
144
145 # update default paths in factor-listener.el for fuel mode
146 substituteInPlace misc/fuel/fuel-listener.el \
147 --replace '(defcustom fuel-factor-root-dir nil' "(defcustom fuel-factor-root-dir \"$out/lib/factor\""
148 '';
149
150 buildPhase = ''
151 runHook preBuild
152 # Necessary here, because ld.so.cache is needed in its final location during rebuild.
153 mkdir -p $out/bin $out/lib/factor
154 patchShebangs ./build.sh
155 # Factor uses XDG_CACHE_HOME for cache during compilation.
156 # We can't have that. So, set it to $TMPDIR/.cache
157 export XDG_CACHE_HOME=$TMPDIR/.cache && mkdir -p $XDG_CACHE_HOME
158
159 # There is no ld.so.cache in NixOS so we construct one
160 # out of known libraries. The side effect is that find-lib
161 # will work only on the known libraries. There does not seem
162 # to be a generic solution here.
163 find $(echo ${lib.makeLibraryPath runtimeLibs} | sed -e 's#:# #g') -name \*.so.\* > $TMPDIR/so.lst
164 (echo $(cat $TMPDIR/so.lst | wc -l) "libs found in cache \`/etc/ld.so.cache'";
165 for l in $(<$TMPDIR/so.lst); do
166 echo " $(basename $l) (libc6,x86-64) => $l";
167 done)> $out/lib/factor/ld.so.cache
168
169 make -j$NIX_BUILD_CORES linux-x86-64
170 printf "First build from upstream boot image\n" >&2
171 ./build.sh bootstrap
172 printf "Rebuild boot image\n" >&2
173 ./factor -script -e='"unix-x86.64" USING: system bootstrap.image memory ; make-image save 0 exit'
174 printf "Second build from local boot image\n" >&2
175 ./build.sh bootstrap
176 runHook postBuild
177 '';
178
179 # For now, the check phase runs, but should always return 0. This way the logs
180 # contain the test failures until all unit tests are fixed. Then, it should
181 # return 1 if any test failures have occurred.
182 doCheck = false;
183 checkPhase = ''
184 runHook preCheck
185 set +e
186 ./factor -e='USING: tools.test zealot.factor sequences namespaces formatting ;
187 zealot-core-vocabs "compiler" suffix [ test ] each :test-failures
188 test-failures get length "Number of failed Tests: %d\n" printf'
189 [ $? -eq 0 ] || {
190 mkdir -p "$out/nix-support"
191 touch "$out/nix-support/failed"
192 }
193 set -e
194 runHook postCheck
195 '';
196
197 installPhase = ''
198 runHook preInstall
199 cp -r factor factor.image LICENSE.txt README.md basis core extra misc $out/lib/factor
200
201 # Create a wrapper in bin/ and lib/factor/
202 ${wrapFactorScript {
203 from = "$out/lib/factor/factor";
204 inherit runtimeLibs;
205 }}
206 mv $out/lib/factor/factor.image $out/lib/factor/.factor-wrapped.image
207 cp $out/lib/factor/factor $out/bin/
208
209 # Emacs fuel expects the image being named `factor.image` in the factor base dir
210 ln -s $out/lib/factor/.factor-wrapped.image $out/lib/factor/factor.image
211
212 # install fuel mode for emacs
213 mkdir -p $out/share/emacs/site-lisp
214 ln -s $out/lib/factor/misc/fuel/*.el $out/share/emacs/site-lisp/
215 runHook postInstall
216 '';
217
218 passthru = {
219 inherit runtimeLibs wrapFactorScript;
220 withLibs = wrapFactor;
221 };
222
223 meta = with lib; {
224 homepage = "https://factorcode.org/";
225 description = "Concatenative, stack-based programming language";
226 longDescription = ''
227 The Factor programming language is a concatenative, stack-based
228 programming language with high-level features including dynamic types,
229 extensible syntax, macros, and garbage collection. On a practical side,
230 Factor has a full-featured library, supports many different platforms, and
231 has been extensively documented.
232
233 The implementation is fully compiled for performance, while still
234 supporting interactive development. Factor applications are portable
235 between all common platforms. Factor can deploy stand-alone applications
236 on all platforms. Full source code for the Factor project is available
237 under a BSD license.
238 '';
239 license = licenses.bsd2;
240 maintainers = with maintainers; [ spacefrogg ];
241 platforms = lib.intersectLists platforms.x86_64 platforms.linux;
242 mainProgram = "factor";
243 };
244}