# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 # HYBRID PACKAGING -- DO NOT "SIMPLIFY" THIS ON A VERSION BUMP. # # 1) Why the binaries come from the "embeddable" tarball and NOT from the .deb: # Upstream's .deb (and the Fedora RPMs) link against Debian-13 sonames that # do not exist on Gentoo: # libcpp-httplib.so.0.41 -- Gentoo builds cpp-httplib as .so.0.50.1 # libwebsockets.so.19 -- Gentoo ships libwebsockets.so.21 # libmbedcrypto.so.16 -- Gentoo installs this as libmbedcrypto-3.so.16 # The "embeddable" tarball links those statically and only needs libraries # Gentoo provides under the very same soname (verified with readelf -d): # libz.so.1 libzstd.so.1 libssl.so.3 libcrypto.so.3 libdrm_amdgpu.so.1 # libstdc++.so.6 libm.so.6 libgcc_s.so.1 libc.so.6 # # 2) Why the .deb is still fetched (unconditionally, amd64 flavour only): # Only *arch-independent data* is taken out of it -- never an ELF object. # The embeddable tarball omits the web UI, the JSON schemas, the examples, # the man pages, the systemd units and architecture_defaults.json. Those # files carry no machine code, so the amd64 .deb serves every arch and can # stay outside the SRC_URI USE-conditionals. # The six resources/*.json present in BOTH archives were verified # byte-identical for 11.5.0 (backend_versions, bench_scenarios, defaults, # server_models, toolDefinitions, vllm_model_config), so the .deb copy is # used for the whole resources/ tree -- it is a strict superset of the # tarball's. Re-check that with cmp before trusting it on the next bump. # # 3) Why the ELF payload lives in /usr/$(get_libdir)/lemonade-server and NOT # directly in /usr/bin: # The embeddable build resolves its data files as / # resources/ ONLY. Unlike the .deb build it has no /usr/share/lemonade-server # fallback compiled in -- grep both binaries: that string is present in the # .deb one and absent from the embeddable one. Installed straight into # /usr/bin it dies at startup with # Error: Failed to open /usr/bin/resources/defaults.json # So the real executables sit next to a "resources" symlink pointing at the # arch-independent tree under /usr/share/lemonade-server, and /usr/bin only # carries symlinks (/proc/self/exe resolves through them to the real path, # so the lookup still lands in the right directory). inherit multilib systemd unpacker DESCRIPTION="Local LLM server with GPU and NPU acceleration (prebuilt binaries)" HOMEPAGE="https://lemonade-server.ai/ https://github.com/lemonade-sdk/lemonade" MY_EMB="lemonade-embeddable-${PV}-ubuntu" LEMONADE_URI="https://github.com/lemonade-sdk/lemonade/releases/download/v${PV}" SRC_URI=" amd64? ( ${LEMONADE_URI}/${MY_EMB}-x64.tar.gz -> ${P}-amd64.tar.gz ) arm64? ( ${LEMONADE_URI}/${MY_EMB}-arm64.tar.gz -> ${P}-arm64.tar.gz ) ${LEMONADE_URI}/lemonade-server_${PV}-debian13_amd64.deb -> ${P}-data.deb " S="${WORKDIR}" # resources/web-app/renderer.bundle.js is the webpack bundle upstream ships # prebuilt: React and KaTeX (MIT), highlight.js (BSD-3-Clause) and some # Apache-2.0 pieces, per the .LICENSE.txt installed next to it. LICENSE="Apache-2.0 BSD MIT" SLOT="0" KEYWORDS="-* ~amd64 ~arm64" IUSE="+systemd" RESTRICT="bindist mirror strip" RDEPEND=" !!sci-ml/lemonade acct-group/lemonade acct-user/lemonade app-arch/unzip app-arch/zstd:= dev-libs/openssl:0/3 sys-libs/zlib:= x11-libs/libdrm[video_cards_amdgpu] " # ar(1) comes from binutils (@system); xz-utils decompresses the deb data.tar.xz. BDEPEND="app-arch/xz-utils" QA_PREBUILT="*" # src_unpack comes from unpacker.eclass: it routes the .tar.gz through the # normal unpacker and the .deb through unpack_deb (ar + data.tar.xz), so no # override is needed here. src_install() { local libdir="/usr/$(get_libdir)/lemonade-server" local datadir="/usr/share/lemonade-server" local emb if use amd64; then emb="${WORKDIR}/${MY_EMB}-x64" elif use arm64; then emb="${WORKDIR}/${MY_EMB}-arm64" else die "unsupported ARCH=${ARCH}: no embeddable tarball for it" fi # --- ELF payload (arch dependent, from the embeddable tarball) --------- exeinto "${libdir}" doexe "${emb}/lemonade" doexe "${emb}/lemond" # --- arch-independent resources (from the .deb) ------------------------ insinto "${datadir}" doins -r usr/share/lemonade-server/resources # The binaries look for "/resources"; point that at the shared # tree so the data is installed FHS-correctly but still found at runtime. dosym -r "${datadir}/resources" "${libdir}/resources" # User-facing commands. dosym -r "${libdir}/lemonade" /usr/bin/lemonade dosym -r "${libdir}/lemond" /usr/bin/lemond # lemond additionally probes this absolute path as a system-wide defaults # file (hardcoded string "/usr/share/lemonade/defaults.json"). It is # byte-identical to resources/defaults.json; ship it so it resolves. insinto /usr/share/lemonade doins usr/share/lemonade/defaults.json # Examples keep upstream's layout: they are referenced from the docs and # some are meant to be run, so no dodoc compression. insinto "${datadir}" doins -r usr/share/lemonade-server/examples fperms +x "${datadir}/examples/migrate-to-systemd.sh" # --- man pages --------------------------------------------------------- # The .deb ships them pre-gzipped; portage does its own compression and # warns about compressed files in docompress-ed directories, so unpack # them first. gunzip usr/share/man/man1/lemonade.1.gz usr/share/man/man1/lemond.1.gz || die doman usr/share/man/man1/lemonade.1 doman usr/share/man/man1/lemond.1 # --- systemd units ----------------------------------------------------- # USE=systemd gates only the unit files: upstream ships no OpenRC service # and the units rely on systemd-only directives (StateDirectory, # AmbientCapabilities=CAP_SYS_RESOURCE), so they are dead weight elsewhere. # acct-user/acct-group stay unconditional -- the daemon is meant to run # under a dedicated account however it is started. if use systemd; then systemd_dounit usr/lib/systemd/system/lemond.service systemd_douserunit usr/lib/systemd/user/lemond.service fi # upstream's sysusers.d drop-in is deliberately not installed: the account # comes from acct-user/lemonade and acct-group/lemonade, same as in # sci-ml/lemonade. # --- configuration ----------------------------------------------------- # Read by the unit via EnvironmentFile=-/etc/lemonade/conf.d/*.conf insinto /etc/lemonade/conf.d doins etc/lemonade/conf.d/zz-secrets.conf } pkg_postinst() { elog "Start the system-wide server with:" elog " systemctl enable --now lemond.service" elog elog "Or run it as your own user (models land in ~/.cache/lemonade):" elog " systemctl --user enable --now lemond.service" elog elog "The web UI is served at http://localhost:13305/ once lemond is up." elog "API keys and HF_TOKEN belong in /etc/lemonade/conf.d/zz-secrets.conf." elog elog "Inference backends (llama.cpp, ROCm, vLLM, ...) are downloaded by" elog "lemond at runtime into its cache directory; they are not packaged." }