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 { stdenv = pkgs.emscriptenStdenv; }).overrideAttrs (old: { 42 buildInputs = old.buildInputs ++ [ pkg-config ]; 43 # we need to reset this setting! 44 env = (old.env or { }) // { 45 NIX_CFLAGS_COMPILE = ""; 46 }; 47 48 configurePhase = '' 49 # FIXME: Some tests require writing at $HOME 50 HOME=$TMPDIR 51 runHook preConfigure 52 53 #export EMCC_DEBUG=2 54 emconfigure ./configure --prefix=$out --shared 55 56 runHook postConfigure 57 ''; 58 59 dontStrip = true; 60 outputs = [ "out" ]; 61 62 buildPhase = '' 63 runHook preBuild 64 65 emmake make 66 67 runHook postBuild 68 ''; 69 70 installPhase = '' 71 runHook preInstall 72 73 emmake make install 74 75 runHook postInstall 76 ''; 77 78 checkPhase = '' 79 runHook preCheck 80 81 echo "================= testing zlib using node =================" 82 83 echo "Compiling a custom test" 84 set -x 85 emcc -O2 -s EMULATE_FUNCTION_POINTER_CASTS=1 test/example.c -DZ_SOLO \ 86 libz.so.${old.version} -I . -o example.js 87 88 echo "Using node to execute the test" 89 ${pkgs.nodejs}/bin/node ./example.js 90 91 set +x 92 if [ $? -ne 0 ]; then 93 echo "test failed for some reason" 94 exit 1; 95 else 96 echo "it seems to work! very good." 97 fi 98 echo "================= /testing zlib using node =================" 99 100 runHook postCheck 101 ''; 102 103 postPatch = pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isDarwin '' 104 substituteInPlace configure \ 105 --replace-fail '/usr/bin/libtool' 'ar' \ 106 --replace-fail 'AR="libtool"' 'AR="ar"' \ 107 --replace-fail 'ARFLAGS="-o"' 'ARFLAGS="-r"' 108 ''; 109}) 110``` 111 112:::{.example #usage-2-pkgs.buildemscriptenpackage} 113 114# Using `pkgs.buildEmscriptenPackage {}` 115 116This `xmlmirror` example features an Emscripten package that is defined completely from this context and no `pkgs.zlib.override` is used. 117 118```nix 119pkgs.buildEmscriptenPackage { 120 pname = "xmlmirror"; 121 version = "1.2.3"; 122 123 buildInputs = [ 124 pkg-config 125 autoconf 126 automake 127 libtool 128 gnumake 129 libxml2 130 nodejs 131 openjdk 132 json_c 133 ]; 134 135 nativeBuildInputs = [ 136 pkg-config 137 writableTmpDirAsHomeHook 138 zlib 139 ]; 140 141 src = pkgs.fetchgit { 142 url = "https://gitlab.com/odfplugfest/xmlmirror.git"; 143 rev = "4fd7e86f7c9526b8f4c1733e5c8b45175860a8fd"; 144 hash = "sha256-i+QgY+5PYVg5pwhzcDnkfXAznBg3e8sWH2jZtixuWsk="; 145 }; 146 147 configurePhase = '' 148 runHook preConfigure 149 150 rm -f fastXmlLint.js* 151 # a fix for ERROR:root:For asm.js, TOTAL_MEMORY must be a multiple of 16MB, was 234217728 152 # https://gitlab.com/odfplugfest/xmlmirror/issues/8 153 sed -e "s/TOTAL_MEMORY=234217728/TOTAL_MEMORY=268435456/g" -i Makefile.emEnv 154 # https://github.com/kripken/emscripten/issues/6344 155 # https://gitlab.com/odfplugfest/xmlmirror/issues/9 156 sed -e "s/\$(JSONC_LDFLAGS) \$(ZLIB_LDFLAGS) \$(LIBXML20_LDFLAGS)/\$(JSONC_LDFLAGS) \$(LIBXML20_LDFLAGS) \$(ZLIB_LDFLAGS) /g" -i Makefile.emEnv 157 # https://gitlab.com/odfplugfest/xmlmirror/issues/11 158 sed -e "s/-o fastXmlLint.js/-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]' -o fastXmlLint.js/g" -i Makefile.emEnv 159 160 runHook postConfigure 161 ''; 162 163 buildPhase = '' 164 runHook preBuild 165 166 make -f Makefile.emEnv 167 168 runHook postBuild 169 ''; 170 171 outputs = [ 172 "out" 173 "doc" 174 ]; 175 176 installPhase = '' 177 runHook preInstall 178 179 mkdir -p $out/share 180 mkdir -p $doc/share/${name} 181 182 cp Demo* $out/share 183 cp -R codemirror-5.12 $out/share 184 cp fastXmlLint.js* $out/share 185 cp *.xsd $out/share 186 cp *.js $out/share 187 cp *.xhtml $out/share 188 cp *.html $out/share 189 cp *.json $out/share 190 cp *.rng $out/share 191 cp README.md $doc/share/${name} 192 193 runHook postInstall 194 ''; 195 196 checkPhase = '' 197 runHook preCheck 198 199 runHook postCheck 200 ''; 201} 202``` 203 204::: 205 206## Debugging {#declarative-debugging} 207 208Use `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. 209 2101. `nix-shell -I nixpkgs=/some/dir/nixpkgs -A emscriptenPackages.libz` 2112. `cd /tmp/` 2123. `unpackPhase` 2134. cd libz-1.2.3 2145. `configurePhase` 2156. `buildPhase` 2167. ... happy hacking...