summary refs log tree commit diff
path: root/pkgs/development/compilers/factor-lang/factor98.nix
blob: 104290684a506080ab585fd150b245d76458243d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
{ lib
, stdenv
, cairo
, curl
, fetchurl
, freealut
, gdk-pixbuf
, git
, glib
, gnome2
, graphviz
, gtk2-x11
, interpreter
, libGL
, libGLU
, libogg
, librsvg
, libvorbis
, makeWrapper
, ncurses
, openal
, openssl
, pango
, pcre
, runCommand
, runtimeShell
, tzdata
, udis86
, unzip
, writeScriptBin
, zlib
}:
let
  runtimeLibs = [
    cairo
    freealut
    gdk-pixbuf
    glib
    gnome2.gtkglext
    graphviz
    gtk2-x11
    libGL
    libGLU
    libogg
    libvorbis
    openal
    openssl
    pango
    pcre
    udis86
    zlib
  ];

  wrapFactorScript = { from, to ? false, runtimeLibs }: ''
    # Set Gdk pixbuf loaders file to the one from the build dependencies here
    unset GDK_PIXBUF_MODULE_FILE
    # Defined in gdk-pixbuf setup hook
    findGdkPixbufLoaders "${librsvg}"

    ${if to then "makeWrapper ${from} ${to}" else "wrapProgram ${from}"} \
      --set GDK_PIXBUF_MODULE_FILE "$GDK_PIXBUF_MODULE_FILE" \
      --argv0 factor \
      --prefix LD_LIBRARY_PATH : /run/opengl-driver/lib:${lib.makeLibraryPath runtimeLibs} \
      --prefix PATH : ${lib.makeBinPath [ graphviz ]}
  '';

  wrapFactor = runtimeLibs:
    runCommand (lib.appendToName "with-libs" interpreter).name
      {
        nativeBuildInputs = [ makeWrapper ];
        buildInputs = [ gdk-pixbuf ];
        passthru.runtimeLibs = runtimeLibs ++ interpreter.runtimeLibs;
      }
      (wrapFactorScript {
        from = "${interpreter}/lib/factor/.factor.wrapped";
        to = "$out/bin/factor";
        runtimeLibs = (runtimeLibs ++ interpreter.runtimeLibs);
      });

  # Development helper for use in nix shell
  wrapLocalFactor = writeScriptBin "wrapFactor" ''
    #!${runtimeShell}
    ${wrapFactorScript { from = "./factor"; inherit runtimeLibs; }}
    ln -sf factor.image .factor-wrapped.image
  '';
  rev = "7999e72aecc3c5bc4019d43dc4697f49678cc3b4";
  version = "0.98";

in
stdenv.mkDerivation {
  pname = "factor-lang";
  inherit version;

  src = fetchurl {
    url = "https://downloads.factorcode.org/releases/${version}/factor-src-${version}.zip";
    sha256 = "01ip9mbnar4sv60d2wcwfz62qaamdvbykxw3gbhzqa25z36vi3ri";
  };

  patches = [
    ./staging-command-line-0.98-pre.patch
    ./workdir-0.98-pre.patch
    ./adjust-paths-in-unit-tests.patch
  ];

  nativeBuildInputs = [ git makeWrapper curl unzip wrapLocalFactor ];
  buildInputs = runtimeLibs;

  postPatch = ''
    sed -ie '4i GIT_LABEL = heads/master-${rev}' GNUmakefile

    # There is no ld.so.cache in NixOS so we patch out calls to that completely.
    # This should work as long as no application code relies on `find-library*`
    # to return a match, which currently is the case and also a justified assumption.

    sed -ie "s#/sbin/ldconfig -p#cat $out/lib/factor/ld.so.cache#g" \
      basis/alien/libraries/finder/linux/linux.factor

    # Some other hard-coded paths to fix:
    sed -i 's#/usr/share/zoneinfo/#${tzdata}/share/zoneinfo/#g' \
      extra/tzinfo/tzinfo.factor

    sed -i 's#/usr/share/terminfo#${ncurses.out}/share/terminfo#g' \
      extra/terminfo/terminfo.factor

    # De-memoize xdg-* functions, otherwise they break the image.
    sed -ie 's/^MEMO:/:/' basis/xdg/xdg.factor

    # update default paths in factor-listener.el for fuel mode
    substituteInPlace misc/fuel/fuel-listener.el \
      --replace '(defcustom fuel-factor-root-dir nil' "(defcustom fuel-factor-root-dir \"$out/lib/factor\""
  '';

  buildPhase = ''
    runHook preBuild
    # Necessary here, because ld.so.cache is needed in its final location during rebuild.
    mkdir -p $out/bin $out/lib/factor
    patchShebangs ./build.sh
    # Factor uses XDG_CACHE_HOME for cache during compilation.
    # We can't have that. So, set it to $TMPDIR/.cache
    export XDG_CACHE_HOME=$TMPDIR/.cache && mkdir -p $XDG_CACHE_HOME

    # There is no ld.so.cache in NixOS so we construct one
    # out of known libraries. The side effect is that find-lib
    # will work only on the known libraries. There does not seem
    # to be a generic solution here.
    find $(echo ${lib.makeLibraryPath runtimeLibs} | sed -e 's#:# #g') -name \*.so.\* > $TMPDIR/so.lst
    (echo $(cat $TMPDIR/so.lst | wc -l) "libs found in cache \`/etc/ld.so.cache'";
      for l in $(<$TMPDIR/so.lst); do
        echo " $(basename $l) (libc6,x86-64) => $l";
      done)> $out/lib/factor/ld.so.cache

    make -j$NIX_BUILD_CORES linux-x86-64
    printf "First build from upstream boot image\n" >&2
    ./build.sh bootstrap
    printf "Rebuild boot image\n" >&2
    ./factor -script -e='"unix-x86.64" USING: system bootstrap.image memory ; make-image save 0 exit'
    printf "Second build from local boot image\n" >&2
    ./build.sh bootstrap
    runHook postBuild
  '';

  # For now, the check phase runs, but should always return 0. This way the logs
  # contain the test failures until all unit tests are fixed. Then, it should
  # return 1 if any test failures have occured.
  doCheck = false;
  checkPhase = ''
    runHook preCheck
    set +e
    ./factor -e='USING: tools.test zealot.factor sequences namespaces formatting ;
      zealot-core-vocabs "compiler" suffix [ test ] each :test-failures
      test-failures get length "Number of failed Tests: %d\n" printf'
    [ $? -eq 0 ] || {
      mkdir -p "$out/nix-support"
      touch "$out/nix-support/failed"
    }
    set -e
    runHook postCheck
  '';

  installPhase = ''
    runHook preInstall
    cp -r factor factor.image LICENSE.txt README.md basis core extra misc $out/lib/factor

    # Create a wrapper in bin/ and lib/factor/
    ${wrapFactorScript { from = "$out/lib/factor/factor"; inherit runtimeLibs; }}
    mv $out/lib/factor/factor.image $out/lib/factor/.factor-wrapped.image
    cp $out/lib/factor/factor $out/bin/

    # Emacs fuel expects the image being named `factor.image` in the factor base dir
    ln -s $out/lib/factor/.factor-wrapped.image $out/lib/factor/factor.image

    # install fuel mode for emacs
    mkdir -p $out/share/emacs/site-lisp
    ln -s $out/lib/factor/misc/fuel/*.el $out/share/emacs/site-lisp/
    runHook postInstall
  '';

  passthru = {
    inherit runtimeLibs wrapFactorScript;
    withLibs = wrapFactor;
  };

  meta = with lib; {
    homepage = "https://factorcode.org/";
    description = "A concatenative, stack-based programming language";
    longDescription = ''
      The Factor programming language is a concatenative, stack-based
      programming language with high-level features including dynamic types,
      extensible syntax, macros, and garbage collection. On a practical side,
      Factor has a full-featured library, supports many different platforms, and
      has been extensively documented.

      The implementation is fully compiled for performance, while still
      supporting interactive development. Factor applications are portable
      between all common platforms. Factor can deploy stand-alone applications
      on all platforms. Full source code for the Factor project is available
      under a BSD license.
    '';
    license = licenses.bsd2;
    maintainers = with maintainers; [ vrthra spacefrogg ];
    platforms = lib.intersectLists platforms.x86_64 platforms.linux;
    mainProgram = "factor";
  };
}