diff --git a/srcpkgs/anki/patches/disable_popup.patch b/srcpkgs/anki/patches/disable_popup.patch deleted file mode 100644 index 8b7a041fc65..00000000000 --- a/srcpkgs/anki/patches/disable_popup.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- a/aqt/update.py -+++ b/aqt/update.py -@@ -31,6 +31,7 @@ class LatestVersionFinder(QThread): - return d - - def run(self): -+ return - if not self.config['updates']: - return - d = self._data() diff --git a/srcpkgs/anki/patches/fix_distutils_not_found.patch b/srcpkgs/anki/patches/fix_distutils_not_found.patch deleted file mode 100644 index 555c89bbf76..00000000000 --- a/srcpkgs/anki/patches/fix_distutils_not_found.patch +++ /dev/null @@ -1,8 +0,0 @@ ---- a/anki/mpv.py -+++ b/anki/mpv.py -@@ -39 +39 @@ import inspect --from distutils.spawn import find_executable # pylint: disable=import-error,no-name-in-module -+from shutil import which -@@ -68 +68 @@ class MPVBase: -- executable = find_executable("mpv") -+ executable = which("mpv") diff --git a/srcpkgs/anki/patches/fix_float.patch b/srcpkgs/anki/patches/fix_float.patch deleted file mode 100644 index 333d76c8efd..00000000000 --- a/srcpkgs/anki/patches/fix_float.patch +++ /dev/null @@ -1,42 +0,0 @@ ---- a/aqt/deckconf.py -+++ b/aqt/deckconf.py -@@ -172,7 +172,7 @@ class DeckConf(QDialog): - f.lrnGradInt.setValue(c['ints'][0]) - f.lrnEasyInt.setValue(c['ints'][1]) - f.lrnEasyInt.setValue(c['ints'][1]) -- f.lrnFactor.setValue(c['initialFactor']/10.0) -+ f.lrnFactor.setValue(c['initialFactor']//10) - f.newOrder.setCurrentIndex(c['order']) - f.newPerDay.setValue(c['perDay']) - f.bury.setChecked(c.get("bury", True)) -@@ -180,7 +180,7 @@ class DeckConf(QDialog): - # rev - c = self.conf['rev'] - f.revPerDay.setValue(c['perDay']) -- f.easyBonus.setValue(c['ease4']*100) -+ f.easyBonus.setValue(int(c['ease4'])*100) - f.fi1.setValue(c['ivlFct']*100) - f.maxIvl.setValue(c['maxIvl']) - f.revplim.setText(self.parentLimText('rev')) -@@ -192,7 +192,7 @@ class DeckConf(QDialog): - # lapse - c = self.conf['lapse'] - f.lapSteps.setText(self.listToUser(c['delays'])) -- f.lapMult.setValue(c['mult']*100) -+ f.lapMult.setValue(int(c['mult'])*100) - f.lapMinInt.setValue(c['minInt']) - f.leechThreshold.setValue(c['leechFails']) - f.leechAction.setCurrentIndex(c['leechAction']) ---- a/aqt/preferences.py -+++ b/aqt/preferences.py -@@ -77,8 +77,8 @@ class Preferences(QDialog): - f.hwAccel.setVisible(False) - else: - f.hwAccel.setChecked(self.mw.pm.glMode() != "software") -- f.lrnCutoff.setValue(qc['collapseTime']/60.0) -- f.timeLimit.setValue(qc['timeLim']/60.0) -+ f.lrnCutoff.setValue(qc['collapseTime']//60) -+ f.timeLimit.setValue(qc['timeLim']//60) - f.showEstimates.setChecked(qc['estTimes']) - f.showProgress.setChecked(qc['dueCounts']) - f.nightMode.setChecked(qc.get("nightMode", False)) diff --git a/srcpkgs/anki/patches/fix_mpv_args.patch b/srcpkgs/anki/patches/fix_mpv_args.patch deleted file mode 100644 index 0b99dd8fe85..00000000000 --- a/srcpkgs/anki/patches/fix_mpv_args.patch +++ /dev/null @@ -1,29 +0,0 @@ ---- a/anki/mpv.py -+++ b/anki/mpv.py -@@ -104,9 +104,9 @@ - """ - self.argv = [self.executable] - self.argv += self.default_argv -- self.argv += ["--input-ipc-server", self._sock_filename] -+ self.argv += ["--input-ipc-server="+self._sock_filename] - if self.window_id is not None: -- self.argv += ["--wid", str(self.window_id)] -+ self.argv += ["--wid="+str(self.window_id)] - - def _start_process(self): - """Start the mpv process. - ---- a/anki/sound.py -+++ b/anki/sound.py -@@ -123,10 +123,7 @@ - - def setMpvConfigBase(base): - mpvConfPath = os.path.join(base, "mpv.conf") -- MpvManager.default_argv += [ -- "--no-config", -- "--include="+mpvConfPath, -- ] -+ MpvManager.default_argv += ["--include="+mpvConfPath] - - mpvManager = None - diff --git a/srcpkgs/anki/patches/fix_profile_save.patch b/srcpkgs/anki/patches/fix_profile_save.patch deleted file mode 100644 index 9d72a1a20e8..00000000000 --- a/srcpkgs/anki/patches/fix_profile_save.patch +++ /dev/null @@ -1,15 +0,0 @@ -Fixes crash related to saving profile data on exit. ---- a/aqt/profiles.py -+++ b/aqt/profiles.py -@@ -160,7 +160,10 @@ a flash drive.""" % self.base) - return up.load() - - def _pickle(self, obj): -- return pickle.dumps(obj, protocol=0) -+ for key, val in obj.items(): -+ if isinstance(val, QByteArray): -+ obj[key] = bytes(val) # type: ignore -+ return pickle.dumps(obj, protocol=4) - - def load(self, name): - assert name != "_global" diff --git a/srcpkgs/anki/patches/fix_unescape.patch b/srcpkgs/anki/patches/fix_unescape.patch deleted file mode 100644 index f7024bc7bf1..00000000000 --- a/srcpkgs/anki/patches/fix_unescape.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/aqt/reviewer.py -+++ b/aqt/reviewer.py -@@ -359,7 +359,7 @@ - cor = stripHTML(cor) - # ensure we don't chomp multiple whitespace - cor = cor.replace(" ", " ") -- cor = parser.unescape(cor) -+ cor = html.unescape(cor) - cor = cor.replace("\xa0", " ") - cor = cor.strip() - given = self.typedAnswer diff --git a/srcpkgs/anki/patches/no-update.patch b/srcpkgs/anki/patches/no-update.patch new file mode 100644 index 00000000000..7dc6228e2c6 --- /dev/null +++ b/srcpkgs/anki/patches/no-update.patch @@ -0,0 +1,13 @@ +diff --git a/qt/aqt/update.py b/qt/aqt/update.py +index 212ddf93d..6f716cc04 100644 +--- a/qt/aqt/update.py ++++ b/qt/aqt/update.py +@@ -11,6 +11,8 @@ from aqt.utils import openLink, show_warning, showText, tr + + + def check_for_update() -> None: ++ return ++ + from aqt import mw + + def do_check(_col: Collection) -> CheckForUpdateResponse: diff --git a/srcpkgs/anki/patches/strip-formatter-deps.patch b/srcpkgs/anki/patches/strip-formatter-deps.patch new file mode 100644 index 00000000000..19cac7342c6 --- /dev/null +++ b/srcpkgs/anki/patches/strip-formatter-deps.patch @@ -0,0 +1,9 @@ +Remove dependency on dev-python/ruff used to format the generated hooks.py +file as it's not relevant in a packaging use case. +--- a/pylib/tools/hookslib.py ++++ b/pylib/tools/hookslib.py +@@ -205,4 +205,3 @@ + + with open(path, "wb") as file: + file.write(code.encode("utf8")) +- subprocess.run([sys.executable, "-m", "ruff", "format", "-q", path], check=True) diff --git a/srcpkgs/anki/patches/strip-python-pip-system-certs.patch b/srcpkgs/anki/patches/strip-python-pip-system-certs.patch new file mode 100644 index 00000000000..6f03a76c145 --- /dev/null +++ b/srcpkgs/anki/patches/strip-python-pip-system-certs.patch @@ -0,0 +1,16 @@ +Remove pip_system_certs import as Void provides this in python3-certifi +--- a/qt/aqt/__init__.py ++++ b/qt/aqt/__init__.py +@@ -16,13 +16,6 @@ if "ANKI_FIRST_RUN" in os.environ: + + first_run_setup() + +-try: +- import pip_system_certs.wrapt_requests +-except ModuleNotFoundError: +- print( +- "Python module pip_system_certs is not installed. System certificate store and custom SSL certificates may not work. See: https://github.com/ankitects/anki/issues/3016" +- ) +- + if sys.version_info[0] < 3 or sys.version_info[1] < 9: + raise Exception("Anki requires Python 3.9+") diff --git a/srcpkgs/anki/patches/strip-type-checking-deps.patch b/srcpkgs/anki/patches/strip-type-checking-deps.patch new file mode 100644 index 00000000000..d9c4e034669 --- /dev/null +++ b/srcpkgs/anki/patches/strip-type-checking-deps.patch @@ -0,0 +1,23 @@ +We don't do type checking (via mypy and related) downstream. +Mypy-protobuf generates mypy stub files from protobuf specs and should +therefore be stripped. +--- a/build/configure/src/python.rs ++++ b/build/configure/src/python.rs +@@ -87,9 +87,7 @@ pub struct GenPythonProto { + impl BuildAction for GenPythonProto { + fn command(&self) -> &str { + "$protoc $ +- --plugin=protoc-gen-mypy=$protoc-gen-mypy $ + --python_out=$builddir/pylib $ +- --mypy_out=$builddir/pylib $ + -Iproto $in" + } + +@@ -107,7 +105,6 @@ impl BuildAction for GenPythonProto { + .collect(); + build.add_inputs("in", &self.proto_files); + build.add_inputs("protoc", inputs![":protoc_binary"]); +- build.add_inputs("protoc-gen-mypy", inputs![":pyenv:protoc-gen-mypy"]); + build.add_outputs("", python_outputs); + } + } diff --git a/srcpkgs/anki/patches/use-RUST_TARGET-for-cross-compilation-and-add-cargo-.patch b/srcpkgs/anki/patches/use-RUST_TARGET-for-cross-compilation-and-add-cargo-.patch new file mode 100644 index 00000000000..388c564d8a0 --- /dev/null +++ b/srcpkgs/anki/patches/use-RUST_TARGET-for-cross-compilation-and-add-cargo-.patch @@ -0,0 +1,69 @@ +From 0487bfe53838eb1d3536d6ce00d533f81ce9df55 Mon Sep 17 00:00:00 2001 +From: Komeil Parseh +Date: Sat, 26 Jul 2025 00:48:34 +0330 +Subject: [PATCH] use RUST_TARGET for cross-compilation and add cargo verbosity + +--- + build/ninja_gen/src/cargo.rs | 2 +- + build/runner/src/build.rs | 25 +++++++++++++++++++++---- + 2 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/build/ninja_gen/src/cargo.rs b/build/ninja_gen/src/cargo.rs +index 2a33977..64df768 100644 +--- a/build/ninja_gen/src/cargo.rs ++++ b/build/ninja_gen/src/cargo.rs +@@ -86,7 +86,7 @@ pub struct CargoBuild<'a> { + + impl BuildAction for CargoBuild<'_> { + fn command(&self) -> &str { +- "cargo build $release_arg $target_arg $cargo_flags $extra_args" ++ "cargo build $release_arg $target_arg $cargo_flags $extra_args --verbose --verbose" + } + + fn files(&mut self, build: &mut impl FilesHandle) { +diff --git a/build/runner/src/build.rs b/build/runner/src/build.rs +index 107be97..6a37cab 100644 +--- a/build/runner/src/build.rs ++++ b/build/runner/src/build.rs +@@ -4,6 +4,7 @@ + use std::env; + use std::fs; + use std::io::Write; ++use std::path::PathBuf; + use std::process::Command; + use std::time::Instant; + +@@ -152,10 +153,26 @@ fn setup_build_root() -> Utf8PathBuf { + } + + fn bootstrap_build() { +- let status = Command::new("cargo") +- .args(["run", "-p", "configure"]) +- .status(); +- assert!(status.expect("ninja").success()); ++ let rust_target = env::var("RUST_TARGET") ++ .expect("RUST_TARGET environment variable not set"); ++ Command::new("cargo") ++ .args([ ++ "build", ++ "-p", ++ "configure", ++ "--target", ++ rust_target.as_str(), ++ "--verbose" ++ ]) ++ .status() ++ .expect("failed to build configure binary"); ++ let path = format!("out/rust/{}/debug/configure", rust_target); ++ let status = Command::new("vtargetrun") ++ .arg(path) ++ .status() ++ .expect("failed to run configure"); ++ ++ assert!(status.success()); + } + + fn maybe_update_buildhash(build_root: &Utf8Path) { +-- +2.50.1 + diff --git a/srcpkgs/anki/template b/srcpkgs/anki/template index 97ad67d6001..2d6109372db 100644 --- a/srcpkgs/anki/template +++ b/srcpkgs/anki/template @@ -1,21 +1,130 @@ # Template file for 'anki' pkgname=anki -version=2.1.15 -revision=7 -build_style=gnu-makefile -depends="python3-PyQt5-webengine python3-requests python3-SQLAlchemy - python3-PyAudio python3-mpv python3-Markdown python3-send2trash - python3-BeautifulSoup4 python3-decorator python3-jsonschema" +version=25.07.5 +revision=1 +_anki_commit="7172b2d26684c7ef9d10e249bd43dc5bf73ae00c" +_i18n_git="8f5566ca5d3597b2b6fe3e2f278fa6009262a39a" +_ftl_git="bc2da83c77749d96f3df8144f00c87d68dd2187a" +build_helper="rust qemu" +hostmakedepends="cargo hatchling pkgconf protobuf python3-pyqt6-devel-tools + python3-wheel ninja nodejs rsync unzip uv yarn" +makedepends="libzstd-devel rust-std sqlite-devel" +depends="lame mpv sqlite python3 python3-PyAudio python3-BeautifulSoup4 python3-waitress python3-requests + python3-decorator python3-markdown2 python3-orjson python3-protobuf python3-pysocks python3-distro + python3-Flask-Cors python3-jsonschema python3-send2trash python3-certifi python3-pyqt6 python3-pyqt6-declarative + python3-pyqt6-gui python3-pyqt6-network python3-pyqt6-printsupport python3-pyqt6-webengine qt6-svg qt6-multimedia" short_desc="Spaced repetition flashcard program" -maintainer="Orphaned " +maintainer="Komeil Parseh " license="AGPL-3.0-or-later" homepage="https://apps.ankiweb.net" -changelog="https://apps.ankiweb.net/docs/changes.html" -distfiles="https://apps.ankiweb.net/downloads/archive/anki-$version-source.tgz" -checksum=5a53760164c77d619f55107a13099cffe620566a7f610b61b6c4b52487f3bb89 +distfiles="https://github.com/ankitects/anki/archive/refs/tags/${version}.tar.gz>anki-${version}.tar.gz + https://github.com/ankitects/anki-core-i18n/archive/${_i18n_git}.tar.gz>anki-core-i18n-${_i18n_git}.tar.gz + https://github.com/ankitects/anki-desktop-ftl/archive/${_ftl_git}.tar.gz>anki-desktop-ftl-${_ftl_git}.tar.gz" +checksum="ad9c0f53c2388680cde23b131d9c2abbf0f5338024ae35d8f34592fdcdf63179 + aaf0663dc4d6aec37a5e872d89ea57c6d85a9a058964c697f8323c9b22523a6b + e81f423f3fbe28bd526d47f7e1b386da26f3f6e00194c3085696180e48915cce" -python_version=3 +if [ "$XBPS_TARGET_WORDSIZE" = "32" ]; then + broken="Anki only supports 64-bit platforms due to Rust runner" +fi -post_install() { +pre_patch() { + cp -a anki-"${version}"/. . + rm -rf anki-"${version}" + + # place translations in build dir + cp -a anki-core-i18n-"${_i18n_git}"/. ftl/core-repo + cp -a anki-desktop-ftl-"${_ftl_git}"/. ftl/qt-repo +} + +do_configure() { + # Remove any corepack references to build with normal system yarn + rm -f yarn.lock + vsed -i 's/"type": "module",/"type": "module"/' package.json + vsed -i '/packageManager/d' package.json + vsed -i 's/"corepack enable yarn"/"true"/' ./build/ninja_gen/src/node.rs +} + +pre_build() { + # runner wants .git/HEAD to be present. + mkdir -p .git ftl/{core-repo,qt-repo}.git out + touch .git/HEAD ftl/{core-repo,qt-repo}.git/HEAD + echo "${_anki_commit}" > ./out/buildhash + + # Fetch node_modules + export YARN_CACHE_FOLDER="$srcdir/$_caches_yarn" + yarn install --modules-folder out/node_modules --ignore-scripts + ln -sf ./out/node_modules ./ + + # use locally-installed versions + export NODE_BINARY=$(command -v node) + export PROTOC_BINARY=$(command -v protoc) + export PYTHON_BINARY=$(command -v python3) + export UV_BINARY=$(command -v uv) + export YARN_BINARY=$(command -v yarn) + + export CARGO_TARGET_DIR=./out/rust + + # anki internal var for optimization + export RELEASE=2 # 1 faster but less optimization build + + export OFFLINE_BUILD=1 + export UV_NO_BUILD_ISOLATION=1 + export UV_OFFLINE=1 + + # mask pip-sync as we provide dependencies ourselves + local venv="./out/pyenv" + python3 -m venv --system-site-packages --without-pip "$venv" + printf '#!/bin/bash\nexit 0' > "$venv/bin/pip-sync" + chmod +x "$venv/bin/pip-sync" + + if [ ! $CROSS_BUILD ]; then + RUST_TARGET=$(rustc -vV | sed -n 's|host: ||p') + fi +} + +do_build() { + cargo build --release --bin runner --target ${RUST_TARGET} --verbose --verbose + vtargetrun out/rust/${RUST_TARGET}/release/runner build wheels +} + +pre_install() { + mkdir -p ./out/dist + + cd ./out/wheels + unzip -d ../dist anki*.whl + unzip -d ../dist aqt*.whl + + # Update the timestamps otherwise the set dates of the wheels + find ../dist -type f -exec touch {} + +} + +do_install() { + vmkdir usr/share/anki/_aqt + vmkdir usr/share/anki/anki + vmkdir usr/share/anki/aqt + vmkdir usr/share/pixmaps + vmkdir usr/share/applications + vmkdir usr/share/man/man1 + vmkdir usr/bin + vmkdir usr/lib/anki + + vcopy ./out/dist/aqt/* "usr/share/anki/aqt/" + vcopy ./out/dist/_aqt/* "usr/share/anki/_aqt/" + vcopy ./out/dist/anki/* "usr/share/anki/anki/" + + vinstall qt/launcher/lin/anki.xpm 644 usr/share/pixmaps/ + vinstall qt/launcher/lin/anki.png 644 usr/share/pixmaps/ + vinstall qt/launcher/lin/anki.desktop 644 usr/share/applications/ + + vbin qt/tools/runanki.system.in anki + vman qt/launcher/lin/anki.1 + + mv "${DESTDIR}/usr/share/anki/anki/_rsbridge.so" "${DESTDIR}/usr/lib/anki/" + + # Create a symlink from the old location to the new one so Anki doesn't break + ln -s /usr/lib/anki/_rsbridge.so "${DESTDIR}/usr/share/anki/anki/_rsbridge.so" + + vsed -i "s|@PREFIX@|/usr|" "${DESTDIR}/usr/bin/anki" vlicense LICENSE }