summaryrefslogtreecommitdiff
path: root/app-misc/anki/anki-25.09.2.ebuild
blob: 14d867a001bb4d068dadc6fd47fb63ecef55a30e (plain)
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# Copyright 2022-2026 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

EAPI=8

DISTUTILS_EXT=1
DISTUTILS_OPTIONAL=1
DISTUTILS_SINGLE_IMPL=1
DISTUTILS_USE_PEP517=hatchling
PYTHON_COMPAT=( python3_{13..14} )

declare -A GIT_CRATES=(
	[linkcheck]='https://github.com/ankitects/linkcheck;184b2ca50ed39ca43da13f0b830a463861adb9ca;linkcheck-%commit%'
	[percent-encoding-iri]='https://github.com/ankitects/rust-url;bb930b8d089f4d30d7d19c12e54e66191de47b88;rust-url-%commit%/percent_encoding'
)
RUST_MIN_VER="1.88.0"

inherit cargo desktop distutils-r1 edo greadme multiprocessing ninja-utils \
	optfeature toolchain-funcs xdg-utils

DESCRIPTION="Smart spaced repetition flashcard program"
HOMEPAGE="https://apps.ankiweb.net/"

declare -A COMMITS=(
	[anki]="3890e12c9e48c028c3f12aa58cb64bd9f8895e30"
	[ftl-core]="480ef0da728c7ea3485c58529ae7ee02be3e5dba"
	[ftl-desktop]="fd5f984785ad07a0d3dbd893ee3d7e3671eaebd6"
)
SRC_URI="${CARGO_CRATE_URIS}
	https://github.com/ankitects/anki/archive/refs/tags/${PV}.tar.gz -> ${P}.gh.tar.gz
	https://github.com/ankitects/anki-core-i18n/archive/${COMMITS[ftl-core]}.tar.gz
	-> anki-core-i18n-${COMMITS[ftl-core]}.gh.tar.gz
	https://github.com/ankitects/anki-desktop-ftl/archive/${COMMITS[ftl-desktop]}.tar.gz
	-> anki-desktop-ftl-${COMMITS[ftl-desktop]}.gh.tar.gz
	https://github.com/gentoo-crate-dist/anki/releases/download/${PV%.*}/${P%.*}-crates.tar.xz
	gui? (
		https://home.cit.tum.de/~salu/distfiles/${P}-node_modules.tar.xz
	)
"
# How to get an up-to-date summary of runtime JS libs' licenses:
# ./node_modules/.bin/license-checker-rseidelsohn --production --excludePackages anki --summary
LICENSE="
	AGPL-3+ BSD public-domain
	gui? ( CC0-1.0 CC-BY-4.0 )
"
# Dependent crate licenses
LICENSE+="
	Apache-2.0 Apache-2.0-with-LLVM-exceptions BSD-2 CC0-1.0
	CDLA-Permissive-2.0 ISC MIT MPL-2.0 Unicode-3.0 Unlicense ZLIB
"
# ring crate
LICENSE+=" openssl"
SLOT="0"
KEYWORDS="~amd64"

IUSE="+gui"
REQUIRED_USE="
	doc? ( gui )
	gui? ( ${PYTHON_REQUIRED_USE} )
"
RESTRICT="!gui? ( test ) !test? ( test )"

# Dependencies:
# Python: python/requirements.{anki,aqt}.in
# If ENABLE_QT5_COMPAT is set at runtime
# additionally depend on PyQt6[dbus,printsupport].
# Qt: qt/{aqt/{sound.py,qt/*.py},tools/build_ui.py}
# app-misc/certificates: The rust backend library is built against
# rustls-native-certs to use the native certificate store.
# No ${PYTHON_DEPS} in DEPEND despite external module because it doesn't link
# against libpython

DEPEND="
	>=app-arch/zstd-1.5.5:=
	dev-db/sqlite:3
"
GUI_RDEPEND="
	${PYTHON_DEPS}
	dev-qt/qtsvg:6
	$(python_gen_cond_dep '
		dev-python/beautifulsoup4[${PYTHON_USEDEP}]
		dev-python/distro[${PYTHON_USEDEP}]
		dev-python/decorator[${PYTHON_USEDEP}]
		dev-python/flask[${PYTHON_USEDEP}]
		dev-python/flask-cors[${PYTHON_USEDEP}]
		dev-python/jsonschema[${PYTHON_USEDEP}]
		dev-python/markdown[${PYTHON_USEDEP}]
		dev-python/protobuf[${PYTHON_USEDEP}]
		>=dev-python/pyqt6-6.6.1[gui,network,opengl,quick,webchannel,widgets,${PYTHON_USEDEP}]
		>=dev-python/pyqt6-sip-13.6.0[${PYTHON_USEDEP}]
		>=dev-python/pyqt6-webengine-6.6.0[widgets,${PYTHON_USEDEP}]
		dev-python/requests[${PYTHON_USEDEP}]
		dev-python/send2trash[${PYTHON_USEDEP}]
		dev-python/waitress[${PYTHON_USEDEP}]
	')
"
RDEPEND="
	${DEPEND}
	app-misc/ca-certificates
	gui? ( ${GUI_RDEPEND} )
"
IDEPEND="
	gui? (
		dev-util/desktop-file-utils
		x11-misc/shared-mime-info
	)
"
BDEPEND="
	>=app-arch/zstd-1.5.5:=
	dev-libs/protobuf[protoc(+)]
	virtual/pkgconfig
	gui? (
		${DISTUTILS_DEPS}
		${PYTHON_DEPS}
		app-alternatives/ninja
		>=net-libs/nodejs-20.12.1
		sys-apps/yarn
		$(python_gen_cond_dep 'dev-python/pyqt6[${PYTHON_USEDEP}]')
	)
	test? (
		${RDEPEND}
		app-text/dvipng
		app-text/texlive
		dev-libs/openssl
		dev-util/cargo-nextest
		$(python_gen_cond_dep 'dev-python/mock[${PYTHON_USEDEP}]')
	)
"

distutils_enable_sphinx python/sphinx \
			dev-python/sphinx-autoapi \
			dev-python/sphinx-rtd-theme

EPYTEST_PLUGINS=()
distutils_enable_tests pytest

PATCHES=(
	"${FILESDIR}"/${P}-no-yarn-install.patch
	"${FILESDIR}"/24.04.1/remove-mypy-protobuf.patch
	"${FILESDIR}"/24.04.1/revert-cert-store-hack.patch
	"${FILESDIR}"/23.12.1/ninja-rules-for-cargo.patch
)

QA_FLAGS_IGNORED="usr/bin/anki-sync-server
	usr/lib/python.*/site-packages/anki/_rsbridge.so"

pkg_setup() {
	export PROTOC_BINARY="${BROOT}"/usr/bin/protoc
	export LIBSQLITE3_SYS_USE_PKG_CONFIG=1
	export ZSTD_SYS_USE_PKG_CONFIG=1
	rust_pkg_setup
	use gui && python-single-r1_pkg_setup
}

python_prepare_all() {
	mv "${WORKDIR}"/node_modules out || die
	mv "${WORKDIR}"/yarn.lock . || die

	# Expected files and directories
	mkdir .git out/env || die
	mkdir -p out/pyenv/bin || die

	if use doc; then
		sed "/^REPO_ROOT/s|=.*|= \"${S}\"|" -i python/sphinx/conf.py || die
	fi

	# Unpin Yarn
	sed -e '/"type": "module"/s/,//' \
		-e '/packageManager/d' -i package.json || die

	# Not running the black formatter on generated files saves a dependency
	sed '/subprocess/d' -i pylib/tools/hookslib.py || die

	# Fix hardcoded runner location
	export CARGO_TARGET_DIR="${S}"/out/rust
	cbuild_dir="$(CHOST=${CBUILD:-${CHOST}} cargo_target_dir)"
	sed "s,rust/release,${cbuild_dir##*out/}," \
		-i build/ninja_gen/src/render.rs || die
	# Separate src_configure from runner build
	sed '/ConfigureBuild/d' -i build/ninja_gen/src/build.rs || die
	distutils-r1_python_prepare_all
}

python_prepare() {
	ln -s "${PYTHON}" out/pyenv/bin/python || die
}

src_prepare() {
	default
	rm -r ftl/{core,qt}-repo || die
	ln -s "${WORKDIR}"/anki-core-i18n-${COMMITS[ftl-core]} ftl/core-repo || die
	ln -s "${WORKDIR}"/anki-desktop-ftl-${COMMITS[ftl-desktop]} ftl/qt-repo || die

	mkdir out || die
	echo -e "${COMMITS[anki]:0:8}" > out/buildhash || die

	# None of our ninja implementations are n2
	sed 's/which::which("n2").*/false,/' -i build/ninja_gen/src/build.rs || die

	use gui && distutils-r1_src_prepare
}

_cbuild_cargo_build() {
	CHOST=${CBUILD:-${CHOST}} cargo_src_compile "${@}"
}

python_configure_all() {
	tc-env_build _cbuild_cargo_build -p configure

	local -x NODE_BINARY="${BROOT}"/usr/bin/node \
	YARN_BINARY="${BROOT}"/usr/bin/yarn \
	UV_BINARY="${BROOT}"/bin/true \
	OFFLINE_BUILD=1
	if ! use debug; then
		if tc-is-lto; then
			local -x RELEASE=2
		else
			local -x RELEASE=1
		fi
	fi
	cargo_env edo "${cbuild_dir}"/configure
	unset cbuild_dir
}

src_configure() {
	cargo_gen_config
	cargo_src_configure
	use gui && distutils-r1_src_configure
}

python_compile() {
	tc-env_build _cbuild_cargo_build -p runner
	cargo_env eninja -f out/build.ninja pylib qt
	declare -A wheel_tags=(
		[pylib]="cp39-abi3-manylinux_2_36_x86_64"
		[qt]="py3-none-any"
	)
	local dir
	for dir in ${!wheel_tags[@]}; do
		pushd ${dir} > /dev/null || die
		local -x ANKI_WHEEL_TAG=${wheel_tags[${dir}]}
		distutils-r1_python_compile
		popd || die
	done
}

src_compile() {
	if use gui; then
		distutils-r1_src_compile
	else
		cargo_src_compile -p anki-sync-server
	fi
}

python_test() {
	epytest qt
	epytest pylib
}

python_test_all() {
	local -x NEXTEST_TEST_THREADS="$(makeopts_jobs)"
	edo "${CARGO}" nextest run $(usev !debug '--release') \
			--color always \
			--all-features \
			--tests \
			--no-fail-fast
	eninja -f out/build.ninja check_vitest
}

src_test() {
	local -x ANKI_TEST_MODE=1
	distutils-r1_src_test
}

python_install_all() {
	pushd qt/launcher/lin > /dev/null || die
	doman anki.1
	doicon anki.{png,xpm}
	domenu anki.desktop
	insinto /usr/share/mime/packages
	doins anki.xml
	popd || die
	distutils-r1_python_install_all
}

src_install() {
	greadme_stdin <<- EOF
	Anki's user manual is located online at https://docs.ankiweb.net/
	Anki's add-on developer manual is located online at https://addon-docs.ankiweb.net/
	EOF

	if use gui; then
		distutils-r1_src_install
	else
		cargo_src_install --path rslib/sync
	fi
}

pkg_postinst() {
	greadme_pkg_postinst
	if use gui; then
		xdg_desktop_database_update
		xdg_mimeinfo_database_update
		optfeature "LaTeX in cards" "app-text/texlive[extra] app-text/dvipng"
		optfeature "sound support" media-video/mpv media-video/mplayer
		optfeature "recording support" "media-sound/lame[frontend] dev-python/pyqt6[multimedia]"
		optfeature "faster database operations" dev-python/orjson
		optfeature "Vulkan driver" "media-libs/vulkan-loader dev-qt/qtbase:6[vulkan]
			dev-qt/qtdeclarative:6[vulkan] dev-qt/qtwebengine:6[vulkan]"

		einfo "You can customize the LaTeX header for your cards to fit your needs:"
		einfo "Notes > Manage Note Types > [select a note type] > Options"
	fi
}

pkg_postrm() {
	if use gui; then
		xdg_desktop_database_update
		xdg_mimeinfo_database_update
	fi
}