# Copyright 2026 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

EAPI=8

inherit desktop pax-utils xdg

# Upstream tags every Linux build with a numeric "build" suffix appended
# after a dash (e.g. 0.4.18-1); the internal app version is the same PV
# with that suffix joined by a plus (0.4.18+1). Both the release directory
# and the .deb basename embed the dashed form, so derive it once here and
# bump BUILD whenever upstream re-spins the same PV.
BUILD=1
MY_PV="${PV}-${BUILD}"

DESCRIPTION="Discover, download, and run LLMs locally (Electron desktop app)"
HOMEPAGE="https://lmstudio.ai/"
SRC_URI="
	amd64? (
		https://installers.lmstudio.ai/linux/x64/${MY_PV}/LM-Studio-${MY_PV}-x64.deb
			-> ${P}_amd64.deb
	)
"

S="${WORKDIR}"

LICENSE="LM-Studio"
SLOT="0"
KEYWORDS="-* ~amd64"

# Proprietary binary blob: the Terms of Use forbid redistribution, and the
# .deb ships prebuilt ELFs (the Electron app, its bundled Chromium libs,
# ffmpeg, Vulkan/SwiftShader) that must NOT be stripped.
RESTRICT="bindist mirror strip test"

# Runtime deps. LM Studio bundles its own Electron 38 / Chromium 140 under
# /opt/LM-Studio, so this list is the set of host libraries those prebuilt
# ELFs actually link against, derived from DT_NEEDED on the `lm-studio`
# binary plus the extra libraries upstream's own .deb `Depends:` field and
# the Electron runtime dlopen() at launch (libnotify, libsecret, libXtst,
# libXScrnSaver, libuuid). libudev is consumed via the systemd/eudev split.
# virtual/libcrypt is pulled by the vendored CPython 3.11 that backs the
# llama.cpp extension (_crypt.cpython-311.so links libcrypt.so).
#
# Intentionally NOT listed: libcuda.so.1. It is needed only by the optional
# NVIDIA CUDA llama.cpp backend and is supplied at runtime by
# x11-drivers/nvidia-drivers (driver-provided; never a package dep). The
# bundled Vulkan and CPU (AVX2) backends need no such library.
RDEPEND="
	|| (
		sys-apps/systemd
		sys-apps/systemd-utils
	)
	app-accessibility/at-spi2-core
	app-crypt/libsecret
	dev-libs/expat
	dev-libs/glib:2
	dev-libs/nspr
	dev-libs/nss
	media-libs/alsa-lib
	media-libs/mesa
	net-print/cups
	sys-apps/dbus
	sys-apps/util-linux
	virtual/libcrypt:=
	x11-libs/cairo
	x11-libs/gdk-pixbuf:2
	x11-libs/gtk+:3
	x11-libs/libnotify
	x11-libs/libX11
	x11-libs/libxcb
	x11-libs/libXcomposite
	x11-libs/libXdamage
	x11-libs/libXext
	x11-libs/libXfixes
	x11-libs/libxkbcommon
	x11-libs/libXrandr
	x11-libs/libXScrnSaver
	x11-libs/libXtst
	x11-libs/pango
	x11-misc/xdg-utils
"
BDEPEND="
	app-arch/xz-utils
"

# The whole application tree is prebuilt; skip QA on it.
QA_PREBUILT="opt/LM-Studio/*"

src_unpack() {
	local deb="${DISTDIR}/${P}_amd64.deb"

	mkdir -p "${S}" || die
	cd "${S}" || die

	# .deb is an ar archive of {debian-binary, control.tar.*, data.tar.xz}.
	ar x "${deb}" || die "ar x failed on ${deb}"
	unpack ./data.tar.xz
}

src_install() {
	# Application tree -> /opt/LM-Studio (upstream's own install prefix, as
	# referenced by the shipped .desktop Exec= and the update-alternatives
	# symlink in the .deb postinst). cp -a preserves the executable bits on
	# the bundled ELFs (lm-studio, chrome-sandbox, chrome_crashpad_handler,
	# the *.so blobs).
	insinto /opt
	mkdir -p "${ED}/opt" || die
	cp -a "${S}/opt/LM-Studio" "${ED}/opt/" || die "Failed to install /opt/LM-Studio tree"

	# Electron's setuid sandbox helper must be setuid-root with mode 4711;
	# otherwise Chromium aborts at startup with "The SUID sandbox helper
	# binary was found, but is not configured correctly". This mirrors the
	# `chmod 4755` in upstream's .deb postinst, tightened to 4711 (no read
	# bit needed) as ::gentoo's Electron packages do.
	fperms 4711 /opt/LM-Studio/chrome-sandbox

	# The main binary is a V8/JIT engine: mark it so a PaX/hardened kernel
	# permits RWX/mprotect. No-op on a vanilla kernel.
	pax-mark m "${ED}/opt/LM-Studio/lm-studio"

	# Convenience launcher on PATH (upstream's postinst points the
	# update-alternatives symlink at this same target).
	dosym ../../opt/LM-Studio/lm-studio /usr/bin/lm-studio

	# Desktop file. Upstream ships three defects we repair:
	#   * a stray lowercase `category=` line (invalid key) alongside the
	#     real `Categories=`,
	#   * `Categories=Development;` dropping the `Utility;` the bogus key
	#     was meant to carry,
	#   * an absolute Exec= path (fine, but we normalise to the PATH name so
	#     the menu entry and the CLI agree).
	local desktop="${S}/usr/share/applications/lm-studio.desktop"
	[[ -f "${desktop}" ]] || die "expected desktop file missing: ${desktop}"
	sed -i \
		-e '/^category=/d' \
		-e 's|^Exec=/opt/LM-Studio/lm-studio |Exec=lm-studio |' \
		-e 's|^Categories=Development;$|Categories=Development;Utility;|' \
		"${desktop}" \
		|| die "sed on desktop file failed"
	domenu "${desktop}"

	# Icon. Upstream ships a single 1024x1024 PNG in a bogus "0x0" hicolor
	# directory; reinstall it into the correct sized bucket so icon themes
	# resolve `Icon=lm-studio`.
	newicon -s 1024 \
		"${S}/usr/share/icons/hicolor/0x0/apps/lm-studio.png" \
		lm-studio.png
}

pkg_postinst() {
	xdg_pkg_postinst

	elog ""
	elog "LM Studio stores models and settings under ~/.lmstudio."
	elog "Launch it from your menu or by running: lm-studio"
	elog ""
	elog "A modern x86-64 CPU with AVX2 is required."
	elog ""
	elog "GPU acceleration: LM Studio ships Vulkan and ROCm/llama.cpp"
	elog "backends and selects a runtime from its in-app settings. On some"
	elog "AMD cards whose ISA is not recognised by the bundled ROCm libs,"
	elog "export HSA_OVERRIDE_GFX_VERSION to the matching gfx version before"
	elog "launching, e.g. for RDNA2 (gfx1030):"
	elog "    HSA_OVERRIDE_GFX_VERSION=10.3.0 lm-studio"
	elog ""
}