summary refs log tree commit diff
path: root/pkgs/development/libraries/science/math/openblas/default.nix
blob: 950402315a9b5d8a9255589967dac10406153f6e (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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
{ lib, stdenv, fetchFromGitHub, fetchpatch, perl, which
# Most packages depending on openblas expect integer width to match
# pointer width, but some expect to use 32-bit integers always
# (for compatibility with reference BLAS).
, blas64 ? null
# Multi-threaded applications must not call a threaded OpenBLAS
# (the only exception is when an application uses OpenMP as its
# *only* form of multi-threading). See
#     https://github.com/xianyi/OpenBLAS/wiki/Faq/4bded95e8dc8aadc70ce65267d1093ca7bdefc4c#multi-threaded
#     https://github.com/xianyi/OpenBLAS/issues/2543
# This flag builds a single-threaded OpenBLAS using the flags
# stated in thre.
, singleThreaded ? false
, buildPackages
# Select a specific optimization target (other than the default)
# See https://github.com/xianyi/OpenBLAS/blob/develop/TargetList.txt
, target ? null
# Select whether DYNAMIC_ARCH is enabled or not.
, dynamicArch ? null
# enable AVX512 optimized kernels.
# These kernels have been a source of trouble in the past.
# Use with caution.
, enableAVX512 ? false
, enableStatic ? stdenv.hostPlatform.isStatic
, enableShared ? !stdenv.hostPlatform.isStatic

# for passthru.tests
, ceres-solver
, giac
, octave
, opencv
, python3
}:

let blas64_ = blas64; in

let
  setTarget = x: if target == null then x else target;
  setDynamicArch = x: if dynamicArch == null then x else dynamicArch;

  # To add support for a new platform, add an element to this set.
  configs = {
    armv6l-linux = {
      BINARY = 32;
      TARGET = setTarget "ARMV6";
      DYNAMIC_ARCH = setDynamicArch false;
      USE_OPENMP = true;
    };

    armv7l-linux = {
      BINARY = 32;
      TARGET = setTarget "ARMV7";
      DYNAMIC_ARCH = setDynamicArch false;
      USE_OPENMP = true;
    };

    aarch64-darwin = {
      BINARY = 64;
      TARGET = setTarget "VORTEX";
      DYNAMIC_ARCH = setDynamicArch true;
      USE_OPENMP = false;
      MACOSX_DEPLOYMENT_TARGET = "11.0";
    };

    aarch64-linux = {
      BINARY = 64;
      TARGET = setTarget "ARMV8";
      DYNAMIC_ARCH = setDynamicArch true;
      USE_OPENMP = true;
    };

    i686-linux = {
      BINARY = 32;
      TARGET = setTarget "P2";
      DYNAMIC_ARCH = setDynamicArch true;
      USE_OPENMP = true;
    };

    x86_64-darwin = {
      BINARY = 64;
      TARGET = setTarget "ATHLON";
      DYNAMIC_ARCH = setDynamicArch true;
      NO_AVX512 = !enableAVX512;
      USE_OPENMP = false;
      MACOSX_DEPLOYMENT_TARGET = "10.7";
    };

    x86_64-linux = {
      BINARY = 64;
      TARGET = setTarget "ATHLON";
      DYNAMIC_ARCH = setDynamicArch true;
      NO_AVX512 = !enableAVX512;
      USE_OPENMP = !stdenv.hostPlatform.isMusl;
    };

    powerpc64le-linux = {
      BINARY = 64;
      TARGET = setTarget "POWER5";
      DYNAMIC_ARCH = setDynamicArch true;
      USE_OPENMP = !stdenv.hostPlatform.isMusl;
    };

    riscv64-linux = {
      BINARY = 64;
      TARGET = setTarget "RISCV64_GENERIC";
      DYNAMIC_ARCH = setDynamicArch false;
      USE_OPENMP = true;
    };

    loongarch64-linux = {
      BINARY = 64;
      TARGET = setTarget "LOONGSONGENERIC";
      DYNAMIC_ARCH = setDynamicArch false;
      USE_OPENMP = true;
    };
  };
in

let
  config =
    configs.${stdenv.hostPlatform.system}
    or (throw "unsupported system: ${stdenv.hostPlatform.system}");
in

let
  blas64 =
    if blas64_ != null
      then blas64_
      else lib.hasPrefix "x86_64" stdenv.hostPlatform.system;
  # Convert flag values to format OpenBLAS's build expects.
  # `toString` is almost what we need other than bools,
  # which we need to map {true -> 1, false -> 0}
  # (`toString` produces empty string `""` for false instead of `0`)
  mkMakeFlagValue = val:
    if !builtins.isBool val then toString val
    else if val then "1" else "0";
  mkMakeFlagsFromConfig = lib.mapAttrsToList (var: val: "${var}=${mkMakeFlagValue val}");

  shlibExt = stdenv.hostPlatform.extensions.sharedLibrary;

in
stdenv.mkDerivation rec {
  pname = "openblas";
  version = "0.3.21";

  outputs = [ "out" "dev" ];

  src = fetchFromGitHub {
    owner = "xianyi";
    repo = "OpenBLAS";
    rev = "v${version}";
    sha256 = "sha256-F6cXPqQai4kA5zrsa8E0Q7dD9zZHlwZ+B16EOGNXoXs=";
  };

  patches = lib.optionals stdenv.hostPlatform.isLoongArch64 [
    # https://github.com/xianyi/OpenBLAS/pull/3626
    (fetchpatch {
      name = "openblas-0.3.21-fix-loong.patch";
      url = "https://gitweb.gentoo.org/repo/gentoo.git/plain/sci-libs/openblas/files/openblas-0.3.21-fix-loong.patch?id=37ee4c70278eb41181f69e175575b0152b941655";
      hash = "sha256-iWy11l3wEvzNV08LbhOjnSPj1SjPH8RMnb3ORz7V+gc";
    })
  ];

  postPatch = ''
    # cc1: error: invalid feature modifier 'sve2' in '-march=armv8.5-a+sve+sve2+bf16'
    substituteInPlace Makefile.arm64 --replace "+sve2+bf16" ""
  '';

  inherit blas64;

  # Some hardening features are disabled due to sporadic failures in
  # OpenBLAS-based programs. The problem may not be with OpenBLAS itself, but
  # with how these flags interact with hardening measures used downstream.
  # In either case, OpenBLAS must only be used by trusted code--it is
  # inherently unsuitable for security-conscious applications--so there should
  # be no objection to disabling these hardening measures.
  hardeningDisable = [
    # don't modify or move the stack
    "stackprotector" "pic"
    # don't alter index arithmetic
    "strictoverflow"
    # don't interfere with dynamic target detection
    "relro" "bindnow"
  ];

  nativeBuildInputs = [
    perl
    which
  ];

  depsBuildBuild = [
    buildPackages.gfortran
    buildPackages.stdenv.cc
  ];

  enableParallelBuilding = true;

  makeFlags = mkMakeFlagsFromConfig (config // {
    FC = "${stdenv.cc.targetPrefix}gfortran";
    CC = "${stdenv.cc.targetPrefix}${if stdenv.cc.isClang then "clang" else "cc"}";
    PREFIX = placeholder "out";
    OPENBLAS_INCLUDE_DIR = "${placeholder "dev"}/include";
    NUM_THREADS = 64;
    INTERFACE64 = blas64;
    NO_STATIC = !enableStatic;
    NO_SHARED = !enableShared;
    CROSS = stdenv.hostPlatform != stdenv.buildPlatform;
    HOSTCC = "cc";
    # Makefile.system only checks defined status
    # This seems to be a bug in the openblas Makefile:
    # on x86_64 it expects NO_BINARY_MODE=
    # but on aarch64 it expects NO_BINARY_MODE=0
    NO_BINARY_MODE = if stdenv.isx86_64
        then toString (stdenv.hostPlatform != stdenv.buildPlatform)
        else stdenv.hostPlatform != stdenv.buildPlatform;
    # This disables automatic build job count detection (which honours neither enableParallelBuilding nor NIX_BUILD_CORES)
    # and uses the main make invocation's job count, falling back to 1 if no parallelism is used.
    # https://github.com/xianyi/OpenBLAS/blob/v0.3.20/getarch.c#L1781-L1792
    MAKE_NB_JOBS = 0;
  } // (lib.optionalAttrs singleThreaded {
    # As described on https://github.com/xianyi/OpenBLAS/wiki/Faq/4bded95e8dc8aadc70ce65267d1093ca7bdefc4c#multi-threaded
    USE_THREAD = false;
    USE_LOCKING = true; # available with openblas >= 0.3.7
    USE_OPENMP = false; # openblas will refuse building with both USE_OPENMP=1 and USE_THREAD=0
  }));

  doCheck = true;
  checkTarget = "tests";

  postInstall = ''
    # Write pkgconfig aliases. Upstream report:
    # https://github.com/xianyi/OpenBLAS/issues/1740
    for alias in blas cblas lapack; do
      cat <<EOF > $out/lib/pkgconfig/$alias.pc
Name: $alias
Version: ${version}
Description: $alias provided by the OpenBLAS package.
Cflags: -I$dev/include
Libs: -L$out/lib -lopenblas
EOF
    done

    # Setup symlinks for blas / lapack
  '' + lib.optionalString enableShared ''
    ln -s $out/lib/libopenblas${shlibExt} $out/lib/libblas${shlibExt}
    ln -s $out/lib/libopenblas${shlibExt} $out/lib/libcblas${shlibExt}
    ln -s $out/lib/libopenblas${shlibExt} $out/lib/liblapack${shlibExt}
    ln -s $out/lib/libopenblas${shlibExt} $out/lib/liblapacke${shlibExt}
  '' + lib.optionalString (stdenv.hostPlatform.isLinux && enableShared) ''
    ln -s $out/lib/libopenblas${shlibExt} $out/lib/libblas${shlibExt}.3
    ln -s $out/lib/libopenblas${shlibExt} $out/lib/libcblas${shlibExt}.3
    ln -s $out/lib/libopenblas${shlibExt} $out/lib/liblapack${shlibExt}.3
    ln -s $out/lib/libopenblas${shlibExt} $out/lib/liblapacke${shlibExt}.3
  '' + lib.optionalString enableStatic ''
    ln -s $out/lib/libopenblas.a $out/lib/libblas.a
    ln -s $out/lib/libopenblas.a $out/lib/libcblas.a
    ln -s $out/lib/libopenblas.a $out/lib/liblapack.a
    ln -s $out/lib/libopenblas.a $out/lib/liblapacke.a
  '';

  passthru.tests = {
    inherit (python3.pkgs) numpy scipy;
    inherit ceres-solver giac octave opencv;
  };

  meta = with lib; {
    description = "Basic Linear Algebra Subprograms";
    license = licenses.bsd3;
    homepage = "https://github.com/xianyi/OpenBLAS";
    platforms = attrNames configs;
    maintainers = with maintainers; [ ttuegel ];
  };
}