summaryrefslogtreecommitdiff
path: root/app-misc/anki/anki-25.02.ebuild
blob: 731449d3aec77e0d43322f1347ed906c99e38cd6 (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
# Copyright 2022-2025 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=no
PYTHON_COMPAT=( python3_{11..13} )

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.82.0"

inherit cargo desktop distutils-r1 eapi9-ver multiprocessing ninja-utils \
	optfeature readme.gentoo-r1 toolchain-funcs xdg

DESCRIPTION="A spaced-repetition memory training program (flash cards)"
HOMEPAGE="https://apps.ankiweb.net/"

declare -A COMMITS=(
	[anki]="038d85b1d9e1896e93a3e4a26f600c79ddc33611"
	[ftl-core]="0fe0162f4a18e8ef2fbac1d9a33af8e38cf7260e"
	[ftl-desktop]="17216b03db7249600542e388bd4ea124478400e5"
)
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://git.sr.ht/~antecrescent/gentoo-files/blob/main/app-misc/anki/${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? ( 0BSD CC-BY-4.0 GPL-3+ )"
# Dependent crate licenses
LICENSE+="
	Apache-2.0 Apache-2.0-with-LLVM-exceptions BSD-2 CC0-1.0 ISC MIT
	MPL-2.0 Unicode-3.0 Unicode-DFS-2016 Unlicense ZLIB
"
# Manually added crate licenses
LICENSE+=" openssl"
SLOT="0"
KEYWORDS="~amd64 ~x86"

IUSE="+gui"
REQUIRED_USE="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} )
"

BDEPEND="
	>=app-arch/zstd-1.5.5:=
	dev-libs/protobuf[protoc(+)]
	virtual/pkgconfig
	gui? (
		${PYTHON_DEPS}
		app-alternatives/ninja
		>=net-libs/nodejs-20.12.1
		sys-apps/yarn
		$(python_gen_cond_dep '
			dev-python/pyqt6[${PYTHON_USEDEP}]
			dev-python/wheel[${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

distutils_enable_tests pytest

PATCHES=(
	"${FILESDIR}"/24.06.3/remove-yarn.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

	# Expected files and directories
	mkdir .git out/env || die
	mkdir -p out/pyenv/bin || die
	ln -s "${PYTHON}" out/pyenv/bin/python || 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
}

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 \
	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 "${cbuild_dir}"/configure || die
	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 wheels
	local w
	for w in out/wheels/*.whl; do
		distutils_wheel_install "${BUILD_DIR}"/install ${w}
	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 nextest_opts=(
		cargo-verbose
		failure-output=immediate
		status-level=all
		test-threads=$(get_makeopts_jobs)
	)
	if [[ ! ${CARGO_TERM_COLOR} ]]; then
		[[ "${NOCOLOR}" = true || "${NOCOLOR}" = yes ]] && nextest_opts+=( color=never )
	fi
	nextest_opts=( ${nextest_opts[@]/#/--} )
	cargo_env cargo nextest run ${nextest_opts[@]} || die

	eninja -f out/build.ninja check_vitest
}

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

python_install_all() {
	local DOC_CONTENTS="Users with add-ons that still rely on Anki's Qt5 GUI
	can temporarily set the environment variable ENABLE_QT5_COMPAT to 1 to have
	Anki install the previous compatibility code. This option has additional
	runtime dependencies. Please take a look at this package's optional runtime
	features for a complete listing.
	\n\nENABLE_QT5_COMPAT may be removed in the future, so this is not a
	long-term solution.
	\n\nAnki's user manual is located online at https://docs.ankiweb.net/
	\nAnki's add-on developer manual is located online at
	https://addon-docs.ankiweb.net/"

	readme.gentoo_create_doc
	pushd qt/bundle/lin > /dev/null || die
	doman anki.1
	doicon anki.{png,xpm}
	domenu anki.desktop
	insinto /usr/share/mime/packages
	doins anki.xml
	popd || die
	python_newscript - anki <<-EOF
		#!${EPREFIX}/usr/bin/python
		import sys
		from aqt import run
		sys.exit(run())
	EOF
	distutils-r1_python_install_all
}

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

pkg_postinst() {
	ver_replacing -lt 24.06.3-r1 && local FORCE_PRINT_ELOG=1
	readme.gentoo_print_elog
	if use gui; then
		xdg_pkg_postinst
		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 "compatibility with Qt5-dependent add-ons" dev-python/pyqt6[dbus,printsupport]
		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
}