1# Emscripten {#emscripten}
2
3[Emscripten](https://github.com/kripken/emscripten): An LLVM-to-JavaScript Compiler
4
5If you want to work with `emcc`, `emconfigure` and `emmake` as you are used to from Ubuntu and similar distributions,
6
7```console
8nix-shell -p emscripten
9```
10
11A few things to note:
12
13* `export EMCC_DEBUG=2` is nice for debugging
14* The build artifact cache in `~/.emscripten` sometimes creates issues and needs to be removed from time to time
15
16## Examples {#declarative-usage}
17
18Let's see two different examples from `pkgs/top-level/emscripten-packages.nix`:
19
20* `pkgs.zlib.override`
21* `pkgs.buildEmscriptenPackage`
22
23A special requirement of the `pkgs.buildEmscriptenPackage` is the `doCheck = true`.
24This means each Emscripten package requires that a [`checkPhase`](#ssec-check-phase) is implemented.
25
26* Use `export EMCC_DEBUG=2` from within a phase to get more detailed debug output what is going wrong.
27* The cache at `~/.emscripten` requires to set `HOME=$TMPDIR` in individual phases.
28 This makes compilation slower but also more deterministic.
29
30::: {.example #usage-1-pkgs.zlib.override}
31
32# Using `pkgs.zlib.override {}`
33
34This example uses `zlib` from Nixpkgs, but instead of compiling **C** to **ELF** it compiles **C** to **JavaScript** since we were using `pkgs.zlib.override` and changed `stdenv` to `pkgs.emscriptenStdenv`.
35
36A few adaptions and hacks were put in place to make it work.
37One advantage is that when `pkgs.zlib` is updated, it will automatically update this package as well.
38
39
40```nix
41(pkgs.zlib.override {
42 stdenv = pkgs.emscriptenStdenv;
43}).overrideAttrs
44 (old: {
45 buildInputs = old.buildInputs ++ [ pkg-config ];
46 # we need to reset this setting!
47 env = (old.env or { }) // {
48 NIX_CFLAGS_COMPILE = "";
49 };
50
51 configurePhase = ''
52 # FIXME: Some tests require writing at $HOME
53 HOME=$TMPDIR
54 runHook preConfigure
55
56 #export EMCC_DEBUG=2
57 emconfigure ./configure --prefix=$out --shared
58
59 runHook postConfigure
60 '';
61
62 dontStrip = true;
63 outputs = [ "out" ];
64
65 buildPhase = ''
66 runHook preBuild
67
68 emmake make
69
70 runHook postBuild
71 '';
72
73 installPhase = ''
74 runHook preInstall
75
76 emmake make install
77
78 runHook postInstall
79 '';
80
81 checkPhase = ''
82 runHook preCheck
83
84 echo "================= testing zlib using node ================="
85
86 echo "Compiling a custom test"
87 set -x
88 emcc -O2 -s EMULATE_FUNCTION_POINTER_CASTS=1 test/example.c -DZ_SOLO \
89 libz.so.${old.version} -I . -o example.js
90
91 echo "Using node to execute the test"
92 ${pkgs.nodejs}/bin/node ./example.js
93
94 set +x
95 if [ $? -ne 0 ]; then
96 echo "test failed for some reason"
97 exit 1;
98 else
99 echo "it seems to work! very good."
100 fi
101 echo "================= /testing zlib using node ================="
102
103 runHook postCheck
104 '';
105
106 postPatch = pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isDarwin ''
107 substituteInPlace configure \
108 --replace-fail '/usr/bin/libtool' 'ar' \
109 --replace-fail 'AR="libtool"' 'AR="ar"' \
110 --replace-fail 'ARFLAGS="-o"' 'ARFLAGS="-r"'
111 '';
112 })
113```
114
115:::{.example #usage-2-pkgs.buildemscriptenpackage}
116
117# Using `pkgs.buildEmscriptenPackage {}`
118
119This `xmlmirror` example features an Emscripten package that is defined completely from this context and no `pkgs.zlib.override` is used.
120
121```nix
122pkgs.buildEmscriptenPackage {
123 pname = "xmlmirror";
124 version = "1.2.3";
125
126 buildInputs = [
127 pkg-config
128 autoconf
129 automake
130 libtool
131 gnumake
132 libxml2
133 nodejs
134 openjdk
135 json_c
136 ];
137
138 nativeBuildInputs = [
139 pkg-config
140 writableTmpDirAsHomeHook
141 zlib
142 ];
143
144 src = pkgs.fetchgit {
145 url = "https://gitlab.com/odfplugfest/xmlmirror.git";
146 rev = "4fd7e86f7c9526b8f4c1733e5c8b45175860a8fd";
147 hash = "sha256-i+QgY+5PYVg5pwhzcDnkfXAznBg3e8sWH2jZtixuWsk=";
148 };
149
150 configurePhase = ''
151 runHook preConfigure
152
153 rm -f fastXmlLint.js*
154 # a fix for ERROR:root:For asm.js, TOTAL_MEMORY must be a multiple of 16MB, was 234217728
155 # https://gitlab.com/odfplugfest/xmlmirror/issues/8
156 sed -e "s/TOTAL_MEMORY=234217728/TOTAL_MEMORY=268435456/g" -i Makefile.emEnv
157 # https://github.com/kripken/emscripten/issues/6344
158 # https://gitlab.com/odfplugfest/xmlmirror/issues/9
159 sed -e "s/\$(JSONC_LDFLAGS) \$(ZLIB_LDFLAGS) \$(LIBXML20_LDFLAGS)/\$(JSONC_LDFLAGS) \$(LIBXML20_LDFLAGS) \$(ZLIB_LDFLAGS) /g" -i Makefile.emEnv
160 # https://gitlab.com/odfplugfest/xmlmirror/issues/11
161 sed -e "s/-o fastXmlLint.js/-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]' -o fastXmlLint.js/g" -i Makefile.emEnv
162
163 runHook postConfigure
164 '';
165
166 buildPhase = ''
167 runHook preBuild
168
169 make -f Makefile.emEnv
170
171 runHook postBuild
172 '';
173
174 outputs = [
175 "out"
176 "doc"
177 ];
178
179 installPhase = ''
180 runHook preInstall
181
182 mkdir -p $out/share
183 mkdir -p $doc/share/${name}
184
185 cp Demo* $out/share
186 cp -R codemirror-5.12 $out/share
187 cp fastXmlLint.js* $out/share
188 cp *.xsd $out/share
189 cp *.js $out/share
190 cp *.xhtml $out/share
191 cp *.html $out/share
192 cp *.json $out/share
193 cp *.rng $out/share
194 cp README.md $doc/share/${name}
195 runHook postInstall
196 '';
197
198 checkPhase = ''
199 runHook preCheck
200
201 runHook postCheck
202 '';
203}
204```
205
206:::
207
208## Debugging {#declarative-debugging}
209
210Use `nix-shell -I nixpkgs=/some/dir/nixpkgs -A emscriptenPackages.libz` and from there you can go trough the individual steps. This makes it easy to build a good `unit test` or list the files of the project.
211
2121. `nix-shell -I nixpkgs=/some/dir/nixpkgs -A emscriptenPackages.libz`
2132. `cd /tmp/`
2143. `unpackPhase`
2154. cd libz-1.2.3
2165. `configurePhase`
2176. `buildPhase`
2187. ... happy hacking...