|
25 | 25 | ]; |
26 | 26 | }; |
27 | 27 |
|
28 | | - # Build ArrayFire from the official binary installer; avoids freeimage entirely. |
29 | | - mkArrayfire = pkgs: pkgs.stdenv.mkDerivation rec { |
| 28 | + # Build ArrayFire from the official Linux binary installer; avoids freeimage entirely. |
| 29 | + mkArrayfireLinux = pkgs: pkgs.stdenv.mkDerivation rec { |
30 | 30 | pname = "arrayfire"; |
31 | 31 | version = "3.10.0"; |
32 | 32 | src = pkgs.fetchurl { |
|
50 | 50 | mkdir -p $out |
51 | 51 | bash $src --exclude-subdir --prefix=$out |
52 | 52 | ''; |
| 53 | + # autoPatchelfIgnoreMissingDeps silences missing-dep errors at build time, |
| 54 | + # but a genuinely-absent dep of libafcpu.so would still make its runtime |
| 55 | + # dlopen fail with LoadLibError. Fail the build loudly if the CPU backend |
| 56 | + # has any unresolved (=> not just intentionally-ignored GPU) dependencies. |
| 57 | + doInstallCheck = true; |
| 58 | + installCheckPhase = '' |
| 59 | + libdir=$out/lib64 |
| 60 | + [ -d "$libdir" ] || libdir=$out/lib |
| 61 | + cpu=$(echo "$libdir"/libafcpu.so* | tr ' ' '\n' | head -n1) |
| 62 | + echo "Checking runtime deps of $cpu" |
| 63 | + if ldd "$cpu" | grep -i 'not found'; then |
| 64 | + echo "ERROR: libafcpu.so has unresolved dependencies" >&2 |
| 65 | + exit 1 |
| 66 | + fi |
| 67 | + ''; |
53 | 68 | meta = { |
54 | 69 | description = "A general-purpose library for parallel and massively-parallel architectures"; |
55 | 70 | platforms = [ "x86_64-linux" ]; |
56 | 71 | }; |
57 | 72 | }; |
58 | 73 |
|
| 74 | + # Build ArrayFire on macOS from the official .pkg installer. ArrayFire has not |
| 75 | + # shipped a macOS binary since 3.8.2 (x86_64 only), so darwin pins that version. |
| 76 | + # The .pkg is a xar archive of component sub-packages, each carrying a |
| 77 | + # gzip+cpio Payload that installs under opt/arrayfire/{include,lib}. |
| 78 | + mkArrayfireDarwin = pkgs: pkgs.stdenv.mkDerivation rec { |
| 79 | + pname = "arrayfire"; |
| 80 | + version = "3.8.2"; |
| 81 | + src = pkgs.fetchurl { |
| 82 | + url = "https://arrayfire.s3.amazonaws.com/${version}/ArrayFire-${version}_OSX_x86_64.pkg"; |
| 83 | + hash = "sha256-MDqpDONbzl+PNu2VS1UTaYL10fpzpt0pv10oxNwgm+k="; |
| 84 | + }; |
| 85 | + nativeBuildInputs = with pkgs; [ xar cpio fixDarwinDylibNames ]; |
| 86 | + # Never strip the prebuilt vendor dylibs: the default strip phase corrupts |
| 87 | + # them (it silently truncated libmkl_core.dylib to 0 bytes, which then made |
| 88 | + # MKL fail to load its computational layer at runtime). |
| 89 | + dontStrip = true; |
| 90 | + unpackPhase = '' |
| 91 | + runHook preUnpack |
| 92 | + xar -xf $src |
| 93 | + runHook postUnpack |
| 94 | + ''; |
| 95 | + # Extract every component Payload (except the heavy CUDA/OpenCL/examples ones |
| 96 | + # we don't ship) into a staging tree, then install only the unified + CPU |
| 97 | + # backends and their bundled runtime deps (MKL, TBB, forge). |
| 98 | + installPhase = '' |
| 99 | + runHook preInstall |
| 100 | + mkdir -p stage |
| 101 | + for comp in ArrayFire-${version}-Darwin-*.pkg; do |
| 102 | + case "$comp" in |
| 103 | + *cuda*|*opencl*|*examples*|*documentation*) continue ;; |
| 104 | + esac |
| 105 | + [ -f "$comp/Payload" ] || continue |
| 106 | + ( cd stage && gzip -dc "../$comp/Payload" | cpio -id --quiet ) |
| 107 | + done |
| 108 | +
|
| 109 | + mkdir -p $out/lib |
| 110 | + cp -R stage/opt/arrayfire/include $out/include |
| 111 | + for pat in 'libaf.*' 'libafcpu.*' 'libforge.*' 'libmkl_*.dylib' \ |
| 112 | + 'libtbb*.dylib' 'libiomp*.dylib'; do |
| 113 | + cp -P stage/opt/arrayfire/lib/$pat $out/lib/ 2>/dev/null || true |
| 114 | + done |
| 115 | + runHook postInstall |
| 116 | + ''; |
| 117 | + # fixDarwinDylibNames (run in fixupPhase) rewrites the @rpath install ids |
| 118 | + # and matching inter-library references to absolute store paths. It only |
| 119 | + # rewrites references whose leaf matches a sibling's *original* id, so it |
| 120 | + # misses cases where the ids differ, e.g. libafcpu -> @rpath/libmkl_rt and |
| 121 | + # libmkl_tbb_thread -> @rpath/libtbb (the latter is dlopen'd by MKL's |
| 122 | + # libmkl_rt and would otherwise fail to load at runtime). Re-point any |
| 123 | + # remaining @rpath/<leaf> dep at $out/lib/<leaf> so everything is hermetic. |
| 124 | + postFixup = '' |
| 125 | + for dylib in $out/lib/*.dylib; do |
| 126 | + for dep in $(otool -L "$dylib" | awk 'NR>1{print $1}' | grep '^@rpath/' || true); do |
| 127 | + leaf=''${dep#@rpath/} |
| 128 | + if [ -e "$out/lib/$leaf" ]; then |
| 129 | + install_name_tool -change "$dep" "$out/lib/$leaf" "$dylib" |
| 130 | + fi |
| 131 | + done |
| 132 | + done |
| 133 | + ''; |
| 134 | + meta = { |
| 135 | + description = "A general-purpose library for parallel and massively-parallel architectures"; |
| 136 | + platforms = [ "x86_64-darwin" ]; |
| 137 | + }; |
| 138 | + }; |
| 139 | + |
| 140 | + mkArrayfire = pkgs: |
| 141 | + if pkgs.stdenv.isDarwin |
| 142 | + then mkArrayfireDarwin pkgs |
| 143 | + else mkArrayfireLinux pkgs; |
| 144 | + |
59 | 145 | arrayfire-overlay = self: super: { |
60 | 146 | arrayfire = mkArrayfire self; |
61 | 147 | }; |
|
65 | 151 | haskell = super.haskell // { |
66 | 152 | packageOverrides = inputs.nixpkgs.lib.composeExtensions super.haskell.packageOverrides |
67 | 153 | (hself: hsuper: { |
68 | | - arrayfire = self.haskell.lib.appendConfigureFlags |
69 | | - (hself.callCabal2nix "arrayfire" src { |
70 | | - af = self.arrayfire; |
71 | | - }) |
72 | | - [ "-f disable-default-paths" ]; |
| 154 | + arrayfire = |
| 155 | + let |
| 156 | + pkg = self.haskell.lib.appendConfigureFlags |
| 157 | + (hself.callCabal2nix "arrayfire" src { |
| 158 | + af = self.arrayfire; |
| 159 | + }) |
| 160 | + [ "-f disable-default-paths" ]; |
| 161 | + in |
| 162 | + # On macOS ArrayFire's bundled MKL dlopens its threading layer |
| 163 | + # (libmkl_tbb_thread.dylib) by bare leaf name, which dyld only |
| 164 | + # resolves via DYLD_LIBRARY_PATH. Point it at the arrayfire libs |
| 165 | + # so the test suite (and doctests) can run. Runtime consumers of |
| 166 | + # this package need the same DYLD_LIBRARY_PATH. |
| 167 | + if self.stdenv.isDarwin |
| 168 | + then pkg.overrideAttrs (old: { |
| 169 | + preCheck = (old.preCheck or "") + '' |
| 170 | + export DYLD_LIBRARY_PATH="${self.arrayfire}/lib''${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}" |
| 171 | + ''; |
| 172 | + }) |
| 173 | + # On Linux we link against the unified backend (libaf), which is |
| 174 | + # just a dispatcher that dlopens the real backend impl |
| 175 | + # (libafcpu.so) at runtime. The sandboxed check phase has no |
| 176 | + # LD_LIBRARY_PATH/AF_PATH, so that dlopen finds nothing and every |
| 177 | + # test throws AFException LoadLibError (501). Point the loader at |
| 178 | + # the arrayfire libs so the backend can be found. |
| 179 | + else pkg.overrideAttrs (old: { |
| 180 | + preCheck = (old.preCheck or "") + '' |
| 181 | + export AF_PATH="${self.arrayfire}" |
| 182 | + export LD_LIBRARY_PATH="${self.arrayfire}/lib:${self.arrayfire}/lib64''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" |
| 183 | + ''; |
| 184 | + }); |
73 | 185 | }); |
74 | 186 | }; |
75 | 187 | }; |
76 | 188 |
|
77 | 189 | devShell-for = pkgs: |
78 | 190 | let |
79 | 191 | ps = pkgs.haskellPackages; |
| 192 | + isLinux = pkgs.stdenv.isLinux; |
| 193 | + isDarwin = pkgs.stdenv.isDarwin; |
| 194 | + # ArrayFire only ships an x86_64 macOS binary, so it's unavailable on |
| 195 | + # Apple Silicon; fall back to a plain shell there. |
| 196 | + hasArrayfire = isLinux || pkgs.stdenv.hostPlatform.system == "x86_64-darwin"; |
80 | 197 | in |
81 | | - ps.shellFor { |
82 | | - packages = ps: with ps; [ arrayfire ]; |
83 | | - withHoogle = true; |
84 | | - buildInputs = with pkgs; [ ocl-icd ]; |
85 | | - nativeBuildInputs = with pkgs; with ps; [ |
86 | | - # Building and testing |
87 | | - cabal-install |
88 | | - doctest |
89 | | - hsc2hs |
90 | | - # hspec-discover |
91 | | - nil |
92 | | - # Formatters |
93 | | - nixpkgs-fmt |
94 | | - ]; |
95 | | - shellHook = '' |
96 | | - export LD_LIBRARY_PATH="${pkgs.arrayfire}/lib:$LD_LIBRARY_PATH" |
97 | | - ''; |
98 | | - }; |
| 198 | + ps.shellFor { |
| 199 | + packages = ps: if hasArrayfire then [ ps.arrayfire ] else [ ]; |
| 200 | + withHoogle = true; |
| 201 | + buildInputs = with pkgs; (if isLinux then [ ocl-icd ] else [ darwin.apple_sdk.frameworks.Security ]); |
| 202 | + nativeBuildInputs = with pkgs; with ps; [ |
| 203 | + # Building and testing |
| 204 | + cabal-install |
| 205 | + doctest |
| 206 | + hsc2hs |
| 207 | + # hspec-discover |
| 208 | + nil |
| 209 | + # Formatters |
| 210 | + nixpkgs-fmt |
| 211 | + ]; |
| 212 | + shellHook = |
| 213 | + if isLinux then ''export LD_LIBRARY_PATH="${pkgs.arrayfire}/lib:$LD_LIBRARY_PATH"'' |
| 214 | + else if hasArrayfire then ''export DYLD_LIBRARY_PATH="${pkgs.arrayfire}/lib:$DYLD_LIBRARY_PATH"'' |
| 215 | + else ""; |
| 216 | + }; |
99 | 217 |
|
100 | 218 | pkgs-for = system: import inputs.nixpkgs { |
101 | 219 | inherit system; |
|
107 | 225 | in |
108 | 226 | { |
109 | 227 | packages = inputs.flake-utils.lib.eachDefaultSystemMap (system: |
110 | | - with (pkgs-for system); { |
111 | | - default = haskellPackages.arrayfire; |
| 228 | + let |
| 229 | + pkgs = pkgs-for system; |
| 230 | + # ArrayFire only provides binaries for x86_64-linux and x86_64-darwin |
| 231 | + # (no Apple Silicon / aarch64), so only expose the package there. |
| 232 | + hasArrayfire = pkgs.stdenv.isLinux || system == "x86_64-darwin"; |
| 233 | + in inputs.nixpkgs.lib.optionalAttrs hasArrayfire { |
| 234 | + default = pkgs.haskellPackages.arrayfire; |
112 | 235 | }); |
113 | 236 |
|
114 | 237 | devShells = inputs.flake-utils.lib.eachDefaultSystemMap (system: { |
|
0 commit comments