# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 inherit cmake multilib # The web app (the browser UI served by lemond) is bundled with webpack, and # upstream drives that through `npm ci` at build time. That needs the network, # so the dependency tree is shipped as a separate distfile generated from this # release's src/web-app/package-lock.json with: # npm ci --ignore-scripts --no-audit --no-fund # tar caf lemonade-webapp-node_modules-${PV}.tar.xz node_modules WEBAPP_NODE_MODULES="${PN}-webapp-node_modules-${PV}.tar.xz" DESCRIPTION="Local LLM server with GPU and NPU acceleration" HOMEPAGE="https://lemonade-server.ai/ https://github.com/lemonade-sdk/lemonade" SRC_URI=" https://github.com/lemonade-sdk/lemonade/archive/refs/tags/v${PV}.tar.gz -> ${P}.tar.gz webapp? ( https://distfiles.obentoo.org/${WEBAPP_NODE_MODULES} ) " # The webapp bundle links the JavaScript dependency tree into # renderer.bundle.js: React and KaTeX (MIT), highlight.js (BSD-3-Clause) and # a handful of Apache-2.0 pieces, as listed in the .LICENSE.txt webpack emits # next to the bundle. LICENSE="Apache-2.0 webapp? ( BSD MIT )" SLOT="0" KEYWORDS="~amd64 ~arm64" IUSE="+caps +systemd test +webapp" RESTRICT="!test? ( test )" # Everything upstream cannot find on the system is pulled in with FetchContent # at configure time, which a sandboxed build cannot do, so all of these are # hard dependencies rather than optional ones. libdrm is not optional either: # the Linux branch links drm_amdgpu unconditionally to read AMD GPU memory. COMMON_DEPEND=" >=app-arch/zstd-1.5.5:= >=dev-cpp/cpp-httplib-0.26.0:= >=net-libs/libwebsockets-4.3.3:= >=net-misc/curl-8.5.0:= net-libs/mbedtls:3= x11-libs/libdrm[video_cards_amdgpu] caps? ( sys-libs/libcap ) systemd? ( sys-apps/systemd:= ) " # cli11 and nlohmann_json are header-only. DEPEND=" ${COMMON_DEPEND} >=dev-cpp/cli11-2.4.2 >=dev-cpp/nlohmann_json-3.11.3 " RDEPEND=" ${COMMON_DEPEND} acct-group/lemonade acct-user/lemonade app-arch/unzip !!sci-ml/lemonade-bin " BDEPEND=" virtual/pkgconfig webapp? ( net-libs/nodejs[npm] ) " PATCHES=( "${FILESDIR}"/${P}-webapp-prebuilt-node-modules.patch ) src_unpack() { unpack "${P}.tar.gz" use webapp && unpack "${WEBAPP_NODE_MODULES}" } src_prepare() { cmake_src_prepare # libsystemd (journald log sink) and libcap (capability inheritance for the # backend processes lemond spawns) are probed with bare pkg_check_modules() # calls that have no option() to turn them off, so the detection itself has # to be neutralised or the build links them behind the USE flag's back. if ! use systemd; then sed -i -e 's/pkg_check_modules(SYSTEMD QUIET libsystemd)/set(SYSTEMD_FOUND FALSE)/' \ CMakeLists.txt || die fi if ! use caps; then sed -i -e 's/pkg_check_modules(LIBCAP QUIET libcap)/set(LIBCAP_FOUND FALSE)/' \ CMakeLists.txt || die fi # mbedtls (HTTPS and digest verification in the CLI) is looked up under its # upstream names, but Gentoo suffixes the slot onto everything it installs: # mbedtls-3.pc / libmbedtls-3.so, headers under /usr/include/mbedtls3. The # probe therefore misses it and the fallback find_library() misses it too, # so point pkg-config at the slotted module names instead. Keep this in # sync with the mbedtls slot in COMMON_DEPEND. sed -i -e 's/pkg_check_modules(MBEDTLS QUIET mbedtls mbedx509 mbedcrypto)/pkg_check_modules(MBEDTLS QUIET mbedtls-3 mbedx509-3 mbedcrypto-3)/' \ src/cpp/cli/CMakeLists.txt || die # dev-cpp/cpp-httplib ships a CMake package config (httplib::httplib) but no # pkg-config file, while upstream only probes httplib through # pkg_search_module(). With no .pc the probe fails, USE_SYSTEM_HTTPLIB flips # to OFF and the build tries to FetchContent httplib, which the sandbox # cannot reach. Synthesize the .pc the probe looks for (see the # PKG_CONFIG_PATH export in src_configure). No feature macros in Cflags on # purpose: upstream's system branch skips them too, because they would turn # on header features needing link deps the interface target does not carry. local httplib_ver httplib_ver=$(sed -nE 's/.*CPPHTTPLIB_VERSION[[:space:]]+"([0-9.]+)".*/\1/p' \ "${ESYSROOT}"/usr/include/httplib.h) || die [[ -n ${httplib_ver} ]] || die "could not read cpp-httplib version" mkdir -p "${T}"/pkgconfig || die cat > "${T}"/pkgconfig/httplib.pc <<-EOF || die prefix=${ESYSROOT}/usr libdir=\${prefix}/$(get_libdir) includedir=\${prefix}/include Name: httplib Description: cpp-httplib (Gentoo, synthesized by the ebuild) Version: ${httplib_ver} Cflags: -I\${includedir} Libs: -L\${libdir} -lcpp-httplib EOF } src_configure() { # Let pkg_search_module() find the httplib.pc synthesized in src_prepare. local -x PKG_CONFIG_PATH="${T}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}" local mycmakeargs=( -DBUILD_TESTING=$(usex test) -DBUILD_WEB_APP=$(usex webapp) # The Tauri desktop shell and the AppIndicator tray are separate # products that would each need their own npm/cargo dependency set. -DBUILD_TAURI_APP=OFF -DREQUIRE_LINUX_TRAY=OFF # The web app bundles its own KaTeX from the node_modules distfile; # the system-modules path expects Debian's /usr/share/nodejs layout. -DUSE_SYSTEM_NODEJS_MODULES=OFF # Turn any dependency upstream failed to find into a configure-time # error instead of a silent download attempt. -DFETCHCONTENT_FULLY_DISCONNECTED=ON ) cmake_src_configure } src_compile() { if use webapp; then # Consumed by the patch in src/web-app/BuildWebApp.cmake. local -x LEMONADE_PREBUILT_NODE_MODULES="${WORKDIR}/node_modules" # `npm run build` only spawns the bundled webpack, but keep npm from # touching the network or ${HOME} regardless. local -x npm_config_cache="${T}/npm-cache" local -x npm_config_offline=true local -x npm_config_audit=false local -x npm_config_fund=false local -x npm_config_update_notifier=false fi cmake_src_compile } src_install() { cmake_src_install # The service account comes from acct-user/lemonade and # acct-group/lemonade, so the sysusers.d drop-in only duplicates them. rm -r "${ED}"/usr/lib/sysusers.d || die newinitd "${FILESDIR}"/lemond.initd lemond newconfd "${FILESDIR}"/lemond.confd lemond # /var/lib/lemonade is created at first start, not here: the lemonade # service account does not exist on the build host, so fowners by name would # fail. systemd's StateDirectory= and the initd's checkpath both create it # owned by lemonade:lemonade on startup. } pkg_postinst() { elog "lemond stores models and its config.json under /var/lib/lemonade." elog "Start it with one of:" elog " rc-service lemond start" elog " systemctl start lemond.service" elog elog "Inference backends (llama.cpp, whisper.cpp, vLLM, ONNX Runtime," elog "Ryzen AI/NPU, ...) are not built here: lemond downloads and manages" elog "them at runtime, which is why app-arch/unzip is a runtime dependency." elog elog "Set LEMONADE_API_KEY and HF_TOKEN in /etc/lemonade/conf.d/ for the" elog "systemd unit, or in /etc/conf.d/lemond for the OpenRC service." }