1{ stdenv, bazel_3, buildBazelPackage, isPy3k, lib, fetchFromGitHub, symlinkJoin
2, addOpenGLRunpath, fetchpatch
3# Python deps
4, buildPythonPackage, pythonOlder, python
5# Python libraries
6, numpy, tensorflow-tensorboard_2, absl-py
7, setuptools, wheel, keras-preprocessing, google-pasta
8, opt-einsum, astunparse, h5py
9, termcolor, grpcio, six, wrapt, protobuf, tensorflow-estimator_2
10, dill, flatbuffers-python, tblib, typing-extensions
11# Common deps
12, git, pybind11, which, binutils, glibcLocales, cython, perl
13# Common libraries
14, jemalloc, mpi, gast, grpc, sqlite, boringssl, jsoncpp
15, curl, snappy, flatbuffers-core, lmdb-core, icu, double-conversion, libpng, libjpeg_turbo, giflib
16# Upsteam by default includes cuda support since tensorflow 1.15. We could do
17# that in nix as well. It would make some things easier and less confusing, but
18# it would also make the default tensorflow package unfree. See
19# https://groups.google.com/a/tensorflow.org/forum/#!topic/developers/iRCt5m4qUz0
20, cudaSupport ? false, cudatoolkit ? null, cudnn ? null, nccl ? null
21, mklSupport ? false, mkl ? null
22, tensorboardSupport ? true
23# XLA without CUDA is broken
24, xlaSupport ? cudaSupport
25# Default from ./configure script
26, cudaCapabilities ? [ "sm_35" "sm_50" "sm_60" "sm_70" "sm_75" "compute_80" ]
27, sse42Support ? stdenv.hostPlatform.sse4_2Support
28, avx2Support ? stdenv.hostPlatform.avx2Support
29, fmaSupport ? stdenv.hostPlatform.fmaSupport
30# Darwin deps
31, Foundation, Security
32}:
33
34assert cudaSupport -> cudatoolkit != null
35 && cudnn != null;
36
37# unsupported combination
38assert ! (stdenv.isDarwin && cudaSupport);
39
40assert mklSupport -> mkl != null;
41
42let
43 withTensorboard = (pythonOlder "3.6") || tensorboardSupport;
44
45 cudatoolkit_joined = symlinkJoin {
46 name = "${cudatoolkit.name}-merged";
47 paths = [
48 cudatoolkit.lib
49 cudatoolkit.out
50 ] ++ lib.optionals (lib.versionOlder cudatoolkit.version "11") [
51 # for some reason some of the required libs are in the targets/x86_64-linux
52 # directory; not sure why but this works around it
53 "${cudatoolkit}/targets/${stdenv.system}"
54 ];
55 };
56
57 cudatoolkit_cc_joined = symlinkJoin {
58 name = "${cudatoolkit.cc.name}-merged";
59 paths = [
60 cudatoolkit.cc
61 binutils.bintools # for ar, dwp, nm, objcopy, objdump, strip
62 ];
63 };
64
65 # Needed for _some_ system libraries, grep INCLUDEDIR.
66 includes_joined = symlinkJoin {
67 name = "tensorflow-deps-merged";
68 paths = [
69 jsoncpp
70 ];
71 };
72
73 tfFeature = x: if x then "1" else "0";
74
75 version = "2.4.2";
76 variant = if cudaSupport then "-gpu" else "";
77 pname = "tensorflow${variant}";
78
79 pythonEnv = python.withPackages (_:
80 [ # python deps needed during wheel build time (not runtime, see the buildPythonPackage part for that)
81 # This list can likely be shortened, but each trial takes multiple hours so won't bother for now.
82 absl-py
83 astunparse
84 dill
85 flatbuffers-python
86 gast
87 google-pasta
88 grpcio
89 h5py
90 keras-preprocessing
91 numpy
92 opt-einsum
93 protobuf
94 setuptools
95 six
96 tblib
97 tensorflow-estimator_2
98 tensorflow-tensorboard_2
99 termcolor
100 typing-extensions
101 wheel
102 wrapt
103 ]);
104
105 bazel-build = buildBazelPackage {
106 name = "${pname}-${version}";
107 bazel = bazel_3;
108
109 src = fetchFromGitHub {
110 owner = "tensorflow";
111 repo = "tensorflow";
112 rev = "v${version}";
113 sha256 = "07a2y05hixch1bjag5pzw3p1m7bdj3bq4gdvmsfk2xraz49b1pi8";
114 };
115
116 patches = [
117 # included from 2.6.0 onwards
118 (fetchpatch {
119 name = "fix-numpy-1.20-notimplementederror.patch";
120 url = "https://github.com/tensorflow/tensorflow/commit/b258941525f496763d4277045b6513c815720e3a.patch";
121 sha256 = "19f9bzrcfsynk11s2hqvscin5c65zf7r6g3nb10jnimw79vafiry";
122 })
123 # Relax too strict Python packages versions dependencies.
124 ./relax-dependencies.patch
125 # Add missing `io_bazel_rules_docker` dependency.
126 ./workspace.patch
127 ];
128
129 # On update, it can be useful to steal the changes from gentoo
130 # https://gitweb.gentoo.org/repo/gentoo.git/tree/sci-libs/tensorflow
131
132 nativeBuildInputs = [
133 which pythonEnv cython perl
134 ] ++ lib.optional cudaSupport addOpenGLRunpath;
135
136 buildInputs = [
137 jemalloc
138 mpi
139 glibcLocales
140 git
141
142 # libs taken from system through the TF_SYS_LIBS mechanism
143 grpc
144 sqlite
145 boringssl
146 jsoncpp
147 curl
148 pybind11
149 snappy
150 flatbuffers-core
151 icu
152 double-conversion
153 libpng
154 libjpeg_turbo
155 giflib
156 lmdb-core
157 ] ++ lib.optionals cudaSupport [
158 cudatoolkit
159 cudnn
160 ] ++ lib.optionals mklSupport [
161 mkl
162 ] ++ lib.optionals stdenv.isDarwin [
163 Foundation
164 Security
165 ];
166
167 # arbitrarily set to the current latest bazel version, overly careful
168 TF_IGNORE_MAX_BAZEL_VERSION = true;
169
170 # Take as many libraries from the system as possible. Keep in sync with
171 # list of valid syslibs in
172 # https://github.com/tensorflow/tensorflow/blob/master/third_party/systemlibs/syslibs_configure.bzl
173 TF_SYSTEM_LIBS = lib.concatStringsSep "," [
174 "absl_py"
175 "astor_archive"
176 "astunparse_archive"
177 "boringssl"
178 # Not packaged in nixpkgs
179 # "com_github_googleapis_googleapis"
180 # "com_github_googlecloudplatform_google_cloud_cpp"
181 "com_github_grpc_grpc"
182 # Multiple issues with custom protobuf.
183 # First `com_github_googleapis` fails to configure. Can be worked around by disabling `com_github_googleapis`
184 # and related functionality, but then the next error is about "dangling symbolic link", and in general
185 # looks like that's only the beginning: see
186 # https://stackoverflow.com/questions/55578884/how-to-build-tensorflow-1-13-1-with-custom-protobuf
187 # "com_google_protobuf"
188 # Fails with the error: external/org_tensorflow/tensorflow/core/profiler/utils/tf_op_utils.cc:46:49: error: no matching function for call to 're2::RE2::FullMatch(absl::lts_2020_02_25::string_view&, re2::RE2&)'
189 # "com_googlesource_code_re2"
190 "curl"
191 "cython"
192 "dill_archive"
193 "double_conversion"
194 "enum34_archive"
195 "flatbuffers"
196 "functools32_archive"
197 "gast_archive"
198 "gif"
199 "hwloc"
200 "icu"
201 "jsoncpp_git"
202 "libjpeg_turbo"
203 "lmdb"
204 "nasm"
205 # "nsync" # not packaged in nixpkgs
206 "opt_einsum_archive"
207 "org_sqlite"
208 "pasta"
209 "pcre"
210 "png"
211 "pybind11"
212 "six_archive"
213 "snappy"
214 "tblib_archive"
215 "termcolor_archive"
216 "typing_extensions_archive"
217 "wrapt"
218 "zlib"
219 ];
220
221 INCLUDEDIR = "${includes_joined}/include";
222
223 PYTHON_BIN_PATH = pythonEnv.interpreter;
224
225 TF_NEED_GCP = true;
226 TF_NEED_HDFS = true;
227 TF_ENABLE_XLA = tfFeature xlaSupport;
228
229 CC_OPT_FLAGS = " ";
230
231 # https://github.com/tensorflow/tensorflow/issues/14454
232 TF_NEED_MPI = tfFeature cudaSupport;
233
234 TF_NEED_CUDA = tfFeature cudaSupport;
235 TF_CUDA_PATHS = lib.optionalString cudaSupport "${cudatoolkit_joined},${cudnn},${nccl}";
236 GCC_HOST_COMPILER_PREFIX = lib.optionalString cudaSupport "${cudatoolkit_cc_joined}/bin";
237 GCC_HOST_COMPILER_PATH = lib.optionalString cudaSupport "${cudatoolkit_cc_joined}/bin/gcc";
238 TF_CUDA_COMPUTE_CAPABILITIES = lib.concatStringsSep "," cudaCapabilities;
239
240 postPatch = ''
241 # bazel 3.3 should work just as well as bazel 3.1
242 rm -f .bazelversion
243 '' + lib.optionalString (!withTensorboard) ''
244 # Tensorboard pulls in a bunch of dependencies, some of which may
245 # include security vulnerabilities. So we make it optional.
246 # https://github.com/tensorflow/tensorflow/issues/20280#issuecomment-400230560
247 sed -i '/tensorboard ~=/d' tensorflow/tools/pip_package/setup.py
248 '';
249
250 # https://github.com/tensorflow/tensorflow/pull/39470
251 NIX_CFLAGS_COMPILE = [ "-Wno-stringop-truncation" ];
252
253 preConfigure = let
254 opt_flags = []
255 ++ lib.optionals sse42Support ["-msse4.2"]
256 ++ lib.optionals avx2Support ["-mavx2"]
257 ++ lib.optionals fmaSupport ["-mfma"];
258 in ''
259 patchShebangs configure
260
261 # dummy ldconfig
262 mkdir dummy-ldconfig
263 echo "#!${stdenv.shell}" > dummy-ldconfig/ldconfig
264 chmod +x dummy-ldconfig/ldconfig
265 export PATH="$PWD/dummy-ldconfig:$PATH"
266
267 export PYTHON_LIB_PATH="$NIX_BUILD_TOP/site-packages"
268 export CC_OPT_FLAGS="${lib.concatStringsSep " " opt_flags}"
269 mkdir -p "$PYTHON_LIB_PATH"
270
271 # To avoid mixing Python 2 and Python 3
272 unset PYTHONPATH
273 '';
274
275 configurePhase = ''
276 runHook preConfigure
277 ./configure
278 runHook postConfigure
279 '';
280
281 hardeningDisable = [ "format" ];
282
283 bazelBuildFlags = [
284 "--config=opt" # optimize using the flags set in the configure phase
285 ]
286 ++ lib.optionals stdenv.cc.isClang [ "--cxxopt=-x" "--cxxopt=c++" "--host_cxxopt=-x" "--host_cxxopt=c++" ]
287 ++ lib.optionals (mklSupport) [ "--config=mkl" ];
288
289 bazelTarget = "//tensorflow/tools/pip_package:build_pip_package //tensorflow/tools/lib_package:libtensorflow";
290
291 removeRulesCC = false;
292 # Without this Bazel complaints about sandbox violations.
293 dontAddBazelOpts = true;
294
295 fetchAttrs = {
296 # cudaSupport causes fetch of ncclArchive, resulting in different hashes
297 sha256 = if cudaSupport then
298 "10m6qj3kchgxfgb6qh59vc51knm9r9pkng8bf90h00dnggvv8234"
299 else
300 "04a98yrp09nd0p17k0jbzkgjppxs0yma7m5zkfrwgvr4g0w71v68";
301 };
302
303 buildAttrs = {
304 outputs = [ "out" "python" ];
305
306 preBuild = ''
307 patchShebangs .
308 '';
309
310 installPhase = ''
311 mkdir -p "$out"
312 tar -xf bazel-bin/tensorflow/tools/lib_package/libtensorflow.tar.gz -C "$out"
313 # Write pkgconfig file.
314 mkdir "$out/lib/pkgconfig"
315 cat > "$out/lib/pkgconfig/tensorflow.pc" << EOF
316 Name: TensorFlow
317 Version: ${version}
318 Description: Library for computation using data flow graphs for scalable machine learning
319 Requires:
320 Libs: -L$out/lib -ltensorflow
321 Cflags: -I$out/include/tensorflow
322 EOF
323
324 # build the source code, then copy it to $python (build_pip_package
325 # actually builds a symlink farm so we must dereference them).
326 bazel-bin/tensorflow/tools/pip_package/build_pip_package --src "$PWD/dist"
327 cp -Lr "$PWD/dist" "$python"
328 '';
329
330 postFixup = lib.optionalString cudaSupport ''
331 find $out -type f \( -name '*.so' -or -name '*.so.*' \) | while read lib; do
332 addOpenGLRunpath "$lib"
333 done
334 '';
335
336 requiredSystemFeatures = [
337 "big-parallel"
338 ];
339 };
340
341 meta = with lib; {
342 description = "Computation using data flow graphs for scalable machine learning";
343 homepage = "http://tensorflow.org";
344 license = licenses.asl20;
345 maintainers = with maintainers; [ jyp abbradar ];
346 platforms = with platforms; linux ++ darwin;
347 timeout = 86400; # 24 hours, needed for darwin
348 broken = !(xlaSupport -> cudaSupport);
349 };
350 };
351
352in buildPythonPackage {
353 inherit version pname;
354 disabled = !isPy3k;
355
356 src = bazel-build.python;
357
358 # Upstream has a pip hack that results in bin/tensorboard being in both tensorflow
359 # and the propagated input tensorflow-tensorboard, which causes environment collisions.
360 # Another possibility would be to have tensorboard only in the buildInputs
361 # https://github.com/tensorflow/tensorflow/blob/v1.7.1/tensorflow/tools/pip_package/setup.py#L79
362 postInstall = ''
363 rm $out/bin/tensorboard
364 '';
365
366 setupPyGlobalFlags = [ "--project_name ${pname}" ];
367
368 # tensorflow/tools/pip_package/setup.py
369 propagatedBuildInputs = [
370 absl-py
371 astunparse
372 dill
373 flatbuffers-python
374 gast
375 google-pasta
376 grpcio
377 h5py
378 keras-preprocessing
379 numpy
380 opt-einsum
381 protobuf
382 six
383 tblib
384 tensorflow-estimator_2
385 termcolor
386 typing-extensions
387 wrapt
388 ] ++ lib.optionals withTensorboard [
389 tensorflow-tensorboard_2
390 ];
391
392 nativeBuildInputs = lib.optional cudaSupport addOpenGLRunpath;
393
394 postFixup = lib.optionalString cudaSupport ''
395 find $out -type f \( -name '*.so' -or -name '*.so.*' \) | while read lib; do
396 addOpenGLRunpath "$lib"
397
398 patchelf --set-rpath "${cudatoolkit}/lib:${cudatoolkit.lib}/lib:${cudnn}/lib:${nccl}/lib:$(patchelf --print-rpath "$lib")" "$lib"
399 done
400 '';
401
402 # Actual tests are slow and impure.
403 # TODO try to run them anyway
404 # TODO better test (files in tensorflow/tools/ci_build/builds/*test)
405 checkPhase = ''
406 ${python.interpreter} <<EOF
407 # A simple "Hello world"
408 import tensorflow as tf
409 hello = tf.constant("Hello, world!")
410 tf.print(hello)
411
412 # Fit a simple model to random data
413 import numpy as np
414 np.random.seed(0)
415 tf.random.set_seed(0)
416 model = tf.keras.models.Sequential([
417 tf.keras.layers.Dense(1, activation="linear")
418 ])
419 model.compile(optimizer="sgd", loss="mse")
420
421 x = np.random.uniform(size=(1,1))
422 y = np.random.uniform(size=(1,))
423 model.fit(x, y, epochs=1)
424 EOF
425 '';
426 # Regression test for #77626 removed because not more `tensorflow.contrib`.
427
428 passthru = {
429 deps = bazel-build.deps;
430 libtensorflow = bazel-build.out;
431 };
432
433 inherit (bazel-build) meta;
434}