1{
2 lib,
3 stdenv,
4 fetchPypi,
5 buildPythonPackage,
6 isPyPy,
7 pythonOlder,
8
9 # build-system
10 certifi,
11 pkg-config,
12 pybind11,
13 meson-python,
14 setuptools-scm,
15 pytestCheckHook,
16 python,
17 matplotlib,
18 fetchurl,
19
20 # native libraries
21 ffmpeg-headless,
22 freetype,
23 # By default, almost all tests fail due to the fact we use our version of
24 # freetype. We still define use this argument to define the overriden
25 # derivation `matplotlib.passthru.tests.withoutOutdatedFreetype` - which
26 # builds matplotlib with the freetype version they default to, with which all
27 # tests should pass.
28 doCheck ? false,
29 qhull,
30
31 # propagates
32 contourpy,
33 cycler,
34 fonttools,
35 kiwisolver,
36 numpy,
37 packaging,
38 pillow,
39 pyparsing,
40 python-dateutil,
41
42 # optional
43 importlib-resources,
44
45 # GTK3
46 enableGtk3 ? false,
47 cairo,
48 gobject-introspection,
49 gtk3,
50 pycairo,
51 pygobject3,
52
53 # Tk
54 # Darwin has its own "MacOSX" backend, PyPy has tkagg backend and does not support tkinter
55 enableTk ? (!stdenv.hostPlatform.isDarwin && !isPyPy),
56 tcl,
57 tk,
58 tkinter,
59
60 # Ghostscript
61 enableGhostscript ? true,
62 ghostscript,
63
64 # Qt
65 enableQt ? false,
66 pyqt5,
67
68 # Webagg
69 enableWebagg ? false,
70 tornado,
71
72 # nbagg
73 enableNbagg ? false,
74 ipykernel,
75
76 # darwin
77 Cocoa,
78
79 # required for headless detection
80 libX11,
81 wayland,
82
83 # Reverse dependency
84 sage,
85}:
86
87let
88 interactive = enableTk || enableGtk3 || enableQt;
89in
90
91buildPythonPackage rec {
92 version = "3.9.2";
93 pname = "matplotlib";
94 pyproject = true;
95
96 disabled = pythonOlder "3.8";
97
98 src = fetchPypi {
99 inherit pname version;
100 hash = "sha256-lqtDkGJpymSmNmk0EG+gFTRFSmnkcbe/PXkIOYGqq5I=";
101 };
102
103 env.XDG_RUNTIME_DIR = "/tmp";
104
105 # Matplotlib tries to find Tcl/Tk by opening a Tk window and asking the
106 # corresponding interpreter object for its library paths. This fails if
107 # `$DISPLAY` is not set. The fallback option assumes that Tcl/Tk are both
108 # installed under the same path which is not true in Nix.
109 # With the following patch we just hard-code these paths into the install
110 # script.
111 postPatch =
112 ''
113 substituteInPlace pyproject.toml \
114 --replace-fail '"numpy>=2.0.0rc1,<2.3",' ""
115 patchShebangs tools
116 ''
117 + lib.optionalString (stdenv.hostPlatform.isLinux && interactive) ''
118 # fix paths to libraries in dlopen calls (headless detection)
119 substituteInPlace src/_c_internal_utils.cpp \
120 --replace-fail libX11.so.6 ${libX11}/lib/libX11.so.6 \
121 --replace-fail libwayland-client.so.0 ${wayland}/lib/libwayland-client.so.0
122 '';
123
124 nativeBuildInputs = [ pkg-config ] ++ lib.optionals enableGtk3 [ gobject-introspection ];
125
126 buildInputs =
127 [
128 ffmpeg-headless
129 freetype
130 qhull
131 ]
132 ++ lib.optionals enableGhostscript [ ghostscript ]
133 ++ lib.optionals enableGtk3 [
134 cairo
135 gtk3
136 ]
137 ++ lib.optionals enableTk [
138 libX11
139 tcl
140 tk
141 ]
142 ++ lib.optionals stdenv.hostPlatform.isDarwin [ Cocoa ];
143
144 # clang-11: error: argument unused during compilation: '-fno-strict-overflow' [-Werror,-Wunused-command-line-argument]
145 hardeningDisable = lib.optionals stdenv.hostPlatform.isDarwin [ "strictoverflow" ];
146
147 build-system = [
148 certifi
149 numpy
150 pybind11
151 meson-python
152 setuptools-scm
153 ];
154
155 dependencies =
156 [
157 # explicit
158 contourpy
159 cycler
160 fonttools
161 kiwisolver
162 numpy
163 packaging
164 pillow
165 pyparsing
166 python-dateutil
167 ]
168 ++ lib.optionals (pythonOlder "3.10") [ importlib-resources ]
169 ++ lib.optionals enableGtk3 [
170 pycairo
171 pygobject3
172 ]
173 ++ lib.optionals enableQt [ pyqt5 ]
174 ++ lib.optionals enableWebagg [ tornado ]
175 ++ lib.optionals enableNbagg [ ipykernel ]
176 ++ lib.optionals enableTk [ tkinter ];
177
178 mesonFlags = lib.mapAttrsToList lib.mesonBool {
179 system-freetype = true;
180 system-qhull = true;
181 # Otherwise GNU's `ar` binary fails to put symbols from libagg into the
182 # matplotlib shared objects. See:
183 # -https://github.com/matplotlib/matplotlib/issues/28260#issuecomment-2146243663
184 # -https://github.com/matplotlib/matplotlib/issues/28357#issuecomment-2155350739
185 b_lto = false;
186 };
187
188 passthru.tests = {
189 inherit sage;
190 withOutdatedFreetype = matplotlib.override {
191 doCheck = true;
192 freetype = freetype.overrideAttrs (_: {
193 src = fetchurl {
194 url = "https://download.savannah.gnu.org/releases/freetype/freetype-old/freetype-2.6.1.tar.gz";
195 sha256 = "sha256-Cjx9+9ptoej84pIy6OltmHq6u79x68jHVlnkEyw2cBQ=";
196 };
197 patches = [ ];
198 });
199 };
200 };
201
202 pythonImportsCheck = [ "matplotlib" ];
203 inherit doCheck;
204 nativeCheckInputs = [ pytestCheckHook ];
205 preCheck = ''
206 # https://matplotlib.org/devdocs/devel/testing.html#obtain-the-reference-images
207 find lib -name baseline_images -printf '%P\n' | while read p; do
208 cp -r lib/"$p" $out/${python.sitePackages}/"$p"
209 done
210 # Tests will fail without these files as well
211 cp \
212 lib/matplotlib/tests/{mpltest.ttf,cmr10.pfb,Courier10PitchBT-Bold.pfb} \
213 $out/${python.sitePackages}/matplotlib/tests/
214 # https://github.com/NixOS/nixpkgs/issues/255262
215 cd $out
216 '';
217
218 meta = with lib; {
219 description = "Python plotting library, making publication quality plots";
220 homepage = "https://matplotlib.org/";
221 changelog = "https://github.com/matplotlib/matplotlib/releases/tag/v${version}";
222 license = with licenses; [
223 psfl
224 bsd0
225 ];
226 maintainers = with maintainers; [
227 lovek323
228 veprbl
229 ];
230 };
231}