nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at python-updates 229 lines 7.4 kB view raw
1{ 2 lib, 3 stdenv, 4 fetchFromGitHub, 5 python3, 6 nodejs, 7 closurecompiler, 8 jre, 9 binaryen, 10 llvmPackages, 11 symlinkJoin, 12 makeWrapper, 13 replaceVars, 14 buildNpmPackage, 15 nix-update-script, 16 emscripten, 17}: 18 19let 20 pythonWithPsutil = python3.withPackages (ps: [ ps.psutil ]); 21in 22 23stdenv.mkDerivation rec { 24 pname = "emscripten"; 25 version = "4.0.23"; 26 27 llvmEnv = symlinkJoin { 28 name = "emscripten-llvm-${version}"; 29 paths = with llvmPackages; [ 30 clang-unwrapped 31 (lib.getLib clang-unwrapped) 32 lld 33 llvm 34 ]; 35 }; 36 37 nodeModules = buildNpmPackage { 38 name = "emscripten-node-modules-${version}"; 39 inherit pname version src; 40 41 npmDepsHash = "sha256-3P4H30nS6RBe2Bd3aqa2ueLOm/hxSBux53GgJu/D4Xc="; 42 43 dontBuild = true; 44 45 # Copy node_modules directly. 46 installPhase = '' 47 cp -r node_modules $out/ 48 ''; 49 }; 50 51 src = fetchFromGitHub { 52 owner = "emscripten-core"; 53 repo = "emscripten"; 54 hash = "sha256-i65AWbKuh2KsnugGKmmpUON20He2kgPR6EzwKKA09nQ="; 55 rev = version; 56 }; 57 58 strictDeps = true; 59 60 nativeBuildInputs = [ 61 makeWrapper 62 python3 63 ]; 64 buildInputs = [ 65 nodejs 66 ]; 67 68 patches = [ 69 (replaceVars ./0001-emulate-clang-sysroot-include-logic.patch { 70 resourceDir = "${llvmEnv}/lib/clang/${lib.versions.major llvmPackages.llvm.version}/"; 71 }) 72 ]; 73 74 buildPhase = '' 75 runHook preBuild 76 77 # Make Python scripts executable so patchShebangs will patch their shebangs 78 chmod +x *.py tools/*.py 79 80 patchShebangs . 81 82 # emscripten 4.0.12 requires LLVM tip-of-tree instead of LLVM 21 83 sed -i -e "s/EXPECTED_LLVM_VERSION = 22/EXPECTED_LLVM_VERSION = 21.1/g" tools/shared.py 84 85 # Verify LLVM version patch was applied (fail when nixpkgs has LLVM 22+) 86 grep -q "EXPECTED_LLVM_VERSION = 21.1" tools/shared.py || \ 87 (echo "ERROR: LLVM version patch failed - check if still needed" && exit 1) 88 89 # fixes cmake support 90 sed -i -e "s/print \('emcc (Emscript.*\)/sys.stderr.write(\1); sys.stderr.flush()/g" emcc.py 91 92 sed -i "/^def check_sanity/a\\ return" tools/shared.py 93 94 echo "EMSCRIPTEN_ROOT = '$out/share/emscripten'" > .emscripten 95 echo "LLVM_ROOT = '${llvmEnv}/bin'" >> .emscripten 96 echo "NODE_JS = '${nodejs}/bin/node'" >> .emscripten 97 echo "JS_ENGINES = [NODE_JS]" >> .emscripten 98 echo "CLOSURE_COMPILER = ['${closurecompiler}/bin/closure-compiler']" >> .emscripten 99 echo "JAVA = '${jre}/bin/java'" >> .emscripten 100 # to make the test(s) below work 101 # echo "SPIDERMONKEY_ENGINE = []" >> .emscripten 102 echo "BINARYEN_ROOT = '${binaryen}'" >> .emscripten 103 104 # make emconfigure/emcmake use the correct (wrapped) binaries 105 sed -i "s|^EMCC =.*|EMCC='$out/bin/emcc'|" tools/shared.py 106 sed -i "s|^EMXX =.*|EMXX='$out/bin/em++'|" tools/shared.py 107 sed -i "s|^EMAR =.*|EMAR='$out/bin/emar'|" tools/shared.py 108 sed -i "s|^EMRANLIB =.*|EMRANLIB='$out/bin/emranlib'|" tools/shared.py 109 110 # Remove --no-stack-first flag (not in LLVM 21, added in LLVM 22 when --stack-first became default) 111 # Replace else block with pass to avoid empty block syntax error 112 sed -i "s/cmd.append('--no-stack-first')/pass/" tools/building.py 113 114 # Verify --no-stack-first was removed (fail if patch is no longer needed) 115 grep -q "cmd.append('--no-stack-first')" tools/building.py && \ 116 (echo "ERROR: --no-stack-first patch not needed anymore" && exit 1) || true 117 118 # Fix /tmp symlink issue (macOS: /tmp -> /private/tmp) causing relpath miscalculation 119 sed -i 's/os\.path\.relpath(source_dir, build_dir)/os.path.relpath(source_dir, os.path.realpath(build_dir))/' tools/system_libs.py 120 sed -i 's/os\.path\.relpath(src, build_dir)/os.path.relpath(src, os.path.realpath(build_dir))/' tools/system_libs.py 121 122 # Verify the relpath fix was applied 123 grep -q 'os.path.realpath(build_dir)' tools/system_libs.py || (echo "ERROR: relpath fix not applied" && exit 1) 124 125 # Functional test: verify relpath resolves correctly through symlinks 126 ${python3}/bin/python3 -c " 127 import os, tempfile 128 src = os.path.abspath('tools/system_libs.py') 129 with tempfile.TemporaryDirectory() as tmpdir: 130 build_dir_real = os.path.realpath(tmpdir) 131 relpath = os.path.relpath(src, build_dir_real) 132 resolved = os.path.normpath(os.path.join(build_dir_real, relpath)) 133 assert resolved == src, f'relpath test failed: {resolved} != {src}' 134 print('relpath symlink fix test passed') 135 " 136 137 runHook postBuild 138 ''; 139 140 installPhase = '' 141 runHook preInstall 142 143 appdir=$out/share/emscripten 144 mkdir -p $appdir 145 cp -r . $appdir 146 chmod -R +w $appdir 147 148 mkdir -p $appdir/node_modules/.bin 149 cp -r ${nodeModules}/* $appdir/node_modules 150 cp -r ${nodeModules}/* $appdir/node_modules/.bin 151 152 cp ${./locate_cache.sh} $appdir/locate_cache.sh 153 chmod +x $appdir/locate_cache.sh 154 155 export EM_CACHE=$out/share/emscripten/cache 156 157 mkdir -p $out/bin 158 159 # Wrap all tools consistently via their .py entry points 160 for b in em++ emcc em-config emar embuilder emcmake emconfigure emmake emranlib emrun emscons emsize; do 161 makeWrapper $appdir/$b.py $out/bin/$b \ 162 --set NODE_PATH ${nodeModules} \ 163 --set EM_EXCLUSIVE_CACHE_ACCESS 1 \ 164 --set PYTHON ${python3}/bin/python \ 165 --run "source $appdir/locate_cache.sh" 166 done 167 168 # Create extensionless aliases for tools that need them (e.g., file_packager) 169 for tool in file_packager; do 170 ln -sf $appdir/tools/$tool.py $appdir/tools/$tool 171 done 172 173 # Symlinks for CMake toolchain (expects tools in share/emscripten/) 174 for tool in emcc em++ em-config emar emranlib emcmake emconfigure; do 175 ln -sf $out/bin/$tool $appdir/$tool 176 done 177 178 # precompile libc (etc.) in all variants: 179 pushd $TMPDIR 180 echo 'int __main_argc_argv( int a, int b ) { return 42; }' >test.c 181 for LTO in -flto ""; do 182 for BIND in "" "--bind"; do 183 for PTHREAD in "" "-pthread"; do 184 $out/bin/emcc $LTO $BIND $PTHREAD test.c || true 185 done 186 done 187 done 188 popd 189 190 export PYTHON=${python3}/bin/python 191 export NODE_PATH=${nodeModules} 192 pushd $appdir 193 ${pythonWithPsutil}/bin/python test/runner.py test_hello_world 194 popd 195 196 # fail if any .py files still have unpatched shebangs 197 if grep -l '#!/usr/bin/env' $appdir/*.py $appdir/tools/*.py 2>/dev/null; then 198 echo "ERROR: unpatched shebangs found in .py files" 199 exit 1 200 fi 201 202 runHook postInstall 203 ''; 204 205 passthru = { 206 # HACK: Make emscripten look more like a cc-wrapper to GHC 207 # when building the javascript backend. 208 targetPrefix = "em"; 209 bintools = emscripten; 210 updateScript = nix-update-script { 211 extraArgs = [ 212 "--subpackage" 213 "nodeModules" 214 ]; 215 }; 216 }; 217 218 meta = { 219 homepage = "https://github.com/emscripten-core/emscripten"; 220 description = "LLVM-to-JavaScript Compiler"; 221 platforms = lib.platforms.all; 222 maintainers = with lib.maintainers; [ 223 qknight 224 raitobezarius 225 willcohen 226 ]; 227 license = lib.licenses.ncsa; 228 }; 229}