pytrainer: unpin python 3.10
[NixPkgs.git] / pkgs / games / anki / default.nix
blobd9a4984d3d794dc8f25712cf6fa15368f2558acf
2   lib,
3   stdenv,
5   buildEnv,
6   cargo,
7   fetchFromGitHub,
8   fetchYarnDeps,
9   installShellFiles,
10   lame,
11   mpv-unwrapped,
12   ninja,
13   nixosTests,
14   nodejs,
15   nodejs-slim,
16   fixup-yarn-lock,
17   protobuf,
18   python3,
19   qt6,
20   rsync,
21   rustPlatform,
22   writeShellScriptBin,
23   yarn,
25   AVKit,
26   CoreAudio,
27   swift,
29   mesa,
32 let
33   pname = "anki";
34   version = "24.06.3";
35   rev = "d678e39350a2d243242a69f4e22f5192b04398f2";
37   src = fetchFromGitHub {
38     owner = "ankitects";
39     repo = "anki";
40     rev = version;
41     hash = "sha256-ap8WFDDSGonk5kgXXIsADwAwd7o6Nsy6Wxsa7r1iUIM=";
42     fetchSubmodules = true;
43   };
45   cargoLock = {
46     lockFile = ./Cargo.lock;
47     outputHashes = {
48       "linkcheck-0.4.1" = "sha256-S93J1cDzMlzDjcvz/WABmv8CEC6x78E+f7nzhsN7NkE=";
49       "percent-encoding-iri-2.2.0" = "sha256-kCBeS1PNExyJd4jWfDfctxq6iTdAq69jtxFQgCCQ8kQ=";
50     };
51   };
52   cargoDeps = rustPlatform.importCargoLock cargoLock;
54   yarnOfflineCache = fetchYarnDeps {
55     yarnLock = "${src}/yarn.lock";
56     hash = "sha256-Dbd7RtE0td7li7oqPPfBmAsbXPM8ed9NTAhM5gytpG8=";
57   };
59   anki-build-python = python3.withPackages (ps: with ps; [ mypy-protobuf ]);
61   # anki shells out to git to check its revision, and also to update submodules
62   # We don't actually need the submodules, so we stub that out
63   fakeGit = writeShellScriptBin "git" ''
64     case "$*" in
65       "rev-parse --short=8 HEAD")
66         echo ${builtins.substring 0 8 rev}
67       ;;
68       *"submodule update "*)
69         exit 0
70       ;;
71       *)
72         echo "Unrecognized git: $@"
73         exit 1
74       ;;
75     esac
76   '';
78   # We don't want to run pip-sync, it does network-io
79   fakePipSync = writeShellScriptBin "pip-sync" ''
80     exit 0
81   '';
83   offlineYarn = writeShellScriptBin "yarn" ''
84     [[ "$1" == "install" ]] && exit 0
85     exec ${yarn}/bin/yarn --offline "$@"
86   '';
88   pyEnv = buildEnv {
89     name = "anki-pyenv-${version}";
90     paths = with python3.pkgs; [
91       pip
92       fakePipSync
93       anki-build-python
94     ];
95     pathsToLink = [ "/bin" ];
96   };
98   # https://discourse.nixos.org/t/mkyarnpackage-lockfile-has-incorrect-entry/21586/3
99   anki-nodemodules = stdenv.mkDerivation {
100     pname = "anki-nodemodules";
102     inherit version src yarnOfflineCache;
104     nativeBuildInputs = [
105       nodejs-slim
106       fixup-yarn-lock
107       yarn
108     ];
110     configurePhase = ''
111       export HOME=$NIX_BUILD_TOP
112       yarn config --offline set yarn-offline-mirror $yarnOfflineCache
113       fixup-yarn-lock yarn.lock
114       yarn install --offline --frozen-lockfile --ignore-scripts --no-progress --non-interactive
115       patchShebangs node_modules/
116     '';
118     installPhase = ''
119       mv node_modules $out
120     '';
121   };
123 python3.pkgs.buildPythonApplication {
124   inherit pname version;
126   outputs = [
127     "out"
128     "doc"
129     "man"
130   ];
132   inherit src;
134   patches = [
135     ./patches/disable-auto-update.patch
136     ./patches/remove-the-gl-library-workaround.patch
137     ./patches/skip-formatting-python-code.patch
138     # Also remove from anki/sync-server.nix on next update
139     ./patches/Cargo.lock-update-time-for-rust-1.80.patch
140   ];
142   inherit cargoDeps yarnOfflineCache;
144   nativeBuildInputs = [
145     fakeGit
146     offlineYarn
147     fixup-yarn-lock
149     cargo
150     installShellFiles
151     ninja
152     qt6.wrapQtAppsHook
153     rsync
154     rustPlatform.cargoSetupHook
155   ] ++ lib.optional stdenv.hostPlatform.isDarwin swift;
157   buildInputs = [
158     qt6.qtbase
159     qt6.qtsvg
160   ] ++ lib.optional stdenv.hostPlatform.isLinux qt6.qtwayland;
162   propagatedBuildInputs =
163     with python3.pkgs;
164     [
165       # This rather long list came from running:
166       #    grep --no-filename -oE "^[^ =]*" python/{requirements.base.txt,requirements.bundle.txt,requirements.qt6_lin.txt} | \
167       #      sort | uniq | grep -v "^#$"
168       # in their repo at the git tag for this version
169       # There's probably a more elegant way, but the above extracted all the
170       # names, without version numbers, of their python dependencies. The hope is
171       # that nixpkgs versions are "close enough"
172       # I then removed the ones the check phase failed on (pythonCatchConflictsPhase)
173       attrs
174       beautifulsoup4
175       blinker
176       build
177       certifi
178       charset-normalizer
179       click
180       colorama
181       decorator
182       flask
183       flask-cors
184       google-api-python-client
185       idna
186       importlib-metadata
187       itsdangerous
188       jinja2
189       jsonschema
190       markdown
191       markupsafe
192       orjson
193       packaging
194       pip
195       pip-system-certs
196       pip-tools
197       protobuf
198       pyproject-hooks
199       pyqt6
200       pyqt6-sip
201       pyqt6-webengine
202       pyrsistent
203       pysocks
204       requests
205       send2trash
206       setuptools
207       soupsieve
208       tomli
209       urllib3
210       waitress
211       werkzeug
212       wheel
213       wrapt
214       zipp
215     ]
216     ++ lib.optionals stdenv.hostPlatform.isDarwin [
217       AVKit
218       CoreAudio
219     ];
221   nativeCheckInputs = with python3.pkgs; [
222     pytest
223     mock
224     astroid
225   ];
227   # tests fail with to many open files
228   # TODO: verify if this is still true (I can't, no mac)
229   doCheck = !stdenv.hostPlatform.isDarwin;
231   checkFlags = [
232     # these two tests are flaky, see https://github.com/ankitects/anki/issues/3353
233     # Also removed from anki-sync-server when removing this.
234     "--skip=media::check::test::unicode_normalization"
235     "--skip=scheduler::answering::test::state_application"
236   ];
238   dontUseNinjaInstall = false;
239   dontWrapQtApps = true;
241   env = {
242     # Activate optimizations
243     RELEASE = true;
245     NODE_BINARY = lib.getExe nodejs;
246     PROTOC_BINARY = lib.getExe protobuf;
247     PYTHON_BINARY = lib.getExe python3;
248     YARN_BINARY = lib.getExe offlineYarn;
249   };
251   buildPhase = ''
252     export RUST_BACKTRACE=1
253     export RUST_LOG=debug
255     mkdir -p out/pylib/anki .git
257     echo ${builtins.substring 0 8 rev} > out/buildhash
258     touch out/env
259     touch .git/HEAD
261     ln -vsf ${pyEnv} ./out/pyenv
262     rsync --chmod +w -avP ${anki-nodemodules}/ out/node_modules/
263     ln -vsf out/node_modules node_modules
265     export HOME=$NIX_BUILD_TOP
266     yarn config --offline set yarn-offline-mirror $yarnOfflineCache
267     fixup-yarn-lock yarn.lock
269     patchShebangs ./ninja
270     PIP_USER=1 ./ninja build wheels
271   '';
273   # mimic https://github.com/ankitects/anki/blob/76d8807315fcc2675e7fa44d9ddf3d4608efc487/build/ninja_gen/src/python.rs#L232-L250
274   checkPhase =
275     let
276       disabledTestsString =
277         lib.pipe
278           [
279             # assumes / is not writeable, somehow fails on nix-portable brwap
280             "test_create_open"
281           ]
282           [
283             (lib.map (test: "not ${test}"))
284             (lib.concatStringsSep " and ")
285             lib.escapeShellArg
286           ];
288     in
289     ''
290       runHook preCheck
291       HOME=$TMP ANKI_TEST_MODE=1 PYTHONPATH=$PYTHONPATH:$PWD/out/pylib \
292         pytest -p no:cacheprovider pylib/tests -k ${disabledTestsString}
293       HOME=$TMP ANKI_TEST_MODE=1 PYTHONPATH=$PYTHONPATH:$PWD/out/pylib:$PWD/pylib:$PWD/out/qt \
294         pytest -p no:cacheprovider qt/tests -k ${disabledTestsString}
295       runHook postCheck
296     '';
298   preInstall = ''
299     mkdir dist
300     mv out/wheels/* dist
301   '';
303   postInstall = ''
304     install -D -t $out/share/applications qt/bundle/lin/anki.desktop
305     install -D -t $doc/share/doc/anki README* LICENSE*
306     install -D -t $out/share/mime/packages qt/bundle/lin/anki.xml
307     install -D -t $out/share/pixmaps qt/bundle/lin/anki.{png,xpm}
308     installManPage qt/bundle/lin/anki.1
309   '';
311   preFixup = ''
312     makeWrapperArgs+=(
313       "''${qtWrapperArgs[@]}"
314       --prefix PATH ':' "${lame}/bin:${mpv-unwrapped}/bin"
315     )
316   '';
318   passthru = {
319     # cargoLock is reused in anki-sync-server
320     inherit cargoLock;
321     tests.anki-sync-server = nixosTests.anki-sync-server;
322   };
324   meta = with lib; {
325     description = "Spaced repetition flashcard program";
326     mainProgram = "anki";
327     longDescription = ''
328       Anki is a program which makes remembering things easy. Because it is a lot
329       more efficient than traditional study methods, you can either greatly
330       decrease your time spent studying, or greatly increase the amount you learn.
332       Anyone who needs to remember things in their daily life can benefit from
333       Anki. Since it is content-agnostic and supports images, audio, videos and
334       scientific markup (via LaTeX), the possibilities are endless. For example:
335       learning a language, studying for medical and law exams, memorizing
336       people's names and faces, brushing up on geography, mastering long poems,
337       or even practicing guitar chords!
338     '';
339     homepage = "https://apps.ankiweb.net";
340     license = licenses.agpl3Plus;
341     inherit (mesa.meta) platforms;
342     maintainers = with maintainers; [
343       euank
344       oxij
345     ];
346     # Reported to crash at launch on darwin (as of 2.1.65)
347     broken = stdenv.hostPlatform.isDarwin;
348   };