This is an implementation of the kiss package manager in Emacs lisp.
Currently is WIP, yet usable for my day-to-day use.
Here’s a minimum viable init.el:
;; We require melpa because my tsort package is currently only available there.
(require 'package)
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("elpa" . "https://elpa.gnu.org/packages/")))
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
(require 'use-package)
(use-package tsort :ensure t)
;; Now all of the dependencies are installed, we have to point
;; use-package to the directory where we cloned kiss.el
(setq kiss-site-elisp-dir
"/path/to/cloned/kiss.el/")
(use-package kiss
:ensure t
:if (file-exists-p kiss-site-elisp-dir)
:load-path kiss-site-elisp-dir)
Alternatively, you can use the following use-package declaration if you are using Emacs 30+:
(use-package kiss
:ensure t
:if (>= emacs-major-version 30)
:vc (:url "https://github.com/ehawkvu/kiss.el"
:rev :newest))
Once that (or the equivalent) has been setup inside of Emacs, you will now be able to use the functions defined in kiss.el.
- [X] alternatives
- [X] build
- [X] checksum
- [X] download
- [X] fork
- [X] install^*
- [X] list
- [X] preferred
- [X] remove^*
- [X] search
- [X] update
- [X] upgrade^*
- [X] version
- [X] hooks*
NOTE: Some of these features are partially supported.
Feel free to submit issues & PRs!
install, remove, and upgrade are all rather new in their implementations.
They shouldn’t send your box to /dev/null
, but not promises (yet!).
NOTE: The only three hooks which are not implemented are the pre-source
,
post-source
, and queue-status
hooks. This is because in order to support them,
there would have to be the addition of some ugly hacks (PRs welcome if you can
find a nice elegant way to integrate them), and I’m not entirely convinced of their
usefulness…
Here is a list of some of the extensions that are implemented in kiss.el that currently have no equivalent alternative in upstream kiss.
kiss.el supports performing builds within a temporary chroot that is generated on the fly from the currently running system. The correct files and directories are bound in such a way that the build script will still output files in all the usual spots on the host system, so there are no lost logs.
The two supported sandboxing utilities are bubblewrap
and proot
Additionally, the actual creation of the chroot is also end-user customizable.
Variables of interest:
kiss-make-chroot-strategy
kiss-perform-build-in-sandbox
kiss-sandbox-utility
kiss.el supports using mercurial and fossil as valid repositories and sources.
The format for fossil and mercurial sources is identical to that
of git sources, except with git+
being replaced with fossil+
and hg+
respectively.
This is an attempt to implement something resembling the old style kiss hooks, which took advantage of the fact that kiss was a shell script which could arbitrarily execute shell commands and set environment variables. As per the issue on codeberg here there is some interest in reviving a similar mechanism.
Instead of setting the kiss-hook
variable, you need to set the
kiss-build-env-hook
variable with a list of all of the hooks that you would
like to have set.
**WARNING** I’m not entirely sold on the current idea I have here, and I am liable to change the mechanism in the future. This will especially be the case should upstream change and implement similar functionality, as that mechanism will be preferred.
Each hook in kiss-build-env-hook
will simply take a single argument,
that being the package name.
The environment that each of these hooks could be overwritten by a hook that occurs later in the list.
The actual implementation of these hooks is done by simply adding
eval $(/path/to/kiss-build-env-hook)
to the appropriate part of the
helper build script.
Below illustrates some example code of how you can create a package entirely through using S-Expressions:
;; NOTE: we need to do all of this stuff w/ the build file, since
;; kiss.el expects it to be executable. This *may* change in the future
;; and be toggleable via a variable.
(let ((build-file (kiss--make-temp-file)))
(kiss--write-text
"#!/bin/sh -e
mkdir -p $1/usr/bin
cp -f kiss contrib/* $1/usr/bin/
"
'utf-8
build-file)
(shell-command (concat "chmod +x " build-file))
(kiss--package-build
(kiss-package
:name "kiss-el-test"
:version "git"
:release "1"
:sources
(list
(kiss--string-to-source-obj "git+https://github.com/kiss-community/kiss"))
:build-file build-file))
(shell-command (concat "rm -v " build-file)))
;; NOTE: A potential *future* iteration may look like this:
(kiss--package-build
(kiss-package
:name "kiss-el-test"
:version "git"
:release "1"
:sources
(list
(kiss--string-to-source-obj "git+https://github.com/kiss-community/kiss"))
:build-file
(string-join
'("#!/bin/sh -e"
"mkdir -p $1/usr/bin/"
"cp -f kiss contrib/* $1/usr/bin/")
"\n")))
Since kiss.el is written in Emacs lisp, there are near infinite ways to customize the behavior of the code. In addition to supporting a great deal of variables, kiss.el also implements additional, “redundant” versions of functions that users can “opt-in” to using Emacs’ advice system. Here is an example showing how to use the “fast” version of kiss–get-potential-binary-files:
(advice-add #'kiss--get-potential-binary-files
:override #'kiss--get-potential-binary-files-fast)
The boundaries in this area are endless, as with the advice system you can poke into the brain of any function.
Below illustrates how you can effectively “hold” packages from being updated:
;; This is not a perfect version of "hold", as you can still update the
;; package by explicitly building & installing it, but this will prevent
;; the package from showing up to be built by 'kiss-upgrade'.
(setq kiss-hold-pkgs '("emacs" "firefox" "icu"))
(advice-add
#'kiss--get-out-of-date-pkgs
:around
(lambda (orig &rest args)
"Hold all packages in 'kiss-hold-pkgs'."
(seq-difference (apply orig args) kiss-hold-pkgs)))
Here is a way you can implement a provides system:
;; Here is an example of a somewhat hacky provides system.
;; This uses an alist for setting which packages are "provided"
;; by another.
;; Here we tell kiss that llvm-fat provides the packages
;; llvm, clang, and lld, and that rustup provides rust.
(setq kiss-provides-alist
'(("llvm-fat" . ("llvm" "clang" "lld"))
("rustup" . ("rust"))))
(advice-add
#'kiss--pkg-is-installed-p
:around
(lambda (orig &rest args)
(if (boundp 'kiss-provides-alist)
(apply
orig
(if (member args (flatten-list kiss-provides-alist))
(car
(seq-filter
(lambda (provider)
(member args (assoc provider kiss-provides-alist)))
(mapcar #'car kiss-provides-alist)))
args))
(apply orig args))))
Or, if you would like to use the typical kiss binary for a particular command (let’s say install), you can write the following:
(advice-add
#'kiss-install
:override
(lambda (pkgs-l)
(shell-command
(concat "kiss install "
(mapconcat #'identity (kiss--get-pkg-order pkgs-l) " ")))))
I highly recommend reading up on Emacs’ advice system if you want to have total control over kiss.el or any other Emacs package for that matter.
M-x info RET elisp
- See the section on “Advising Functions”
Here is a good visual resource.
The kiss-compress-alist
can be customized to allow you to
use custom compression commands.
Here is an example adding lz4 support & setting it as the default to kiss.el:
(add-to-list kiss-valid-compress "lz4")
(setq kiss-compress "lz4")
(add-to-list kiss-compress-alist '("lz4" . "lz4 -c"))
(add-to-list kiss-decompress-alist `(,(rx "lz4" eol) . "lz4 -dc"))
Some of these are far more pie-in-the-sky than others. I think that all of them would be cool to have though.
- [X] Create a wrapper script that can be used from the command line
- [X] Write an EIEIO class for packages
- [X] Integrate said EIEIO class throughout the codebase
- [X] Allow for packages to be defined via S-Expressions
- [ ] Write an extensive unit & integration test suite
- [ ] Get this published on MELPA
- [ ] Stabilize the API/come to a consensus as to what is useful.
- [ ] Make a TUI/Menu for installing/upgrading packages
- [ ] Be able to build GUIX derivations
- [ ] Port to Common Lisp
- [ ] Rename to kisp? (once ported to Common Lisp)