+++ Categories = ["Emacs"] Description = "Working out which package archives I'm using" Tags = ["emacs"] title = "Emacs Package Archive Statistics" date = 2014-07-19T13:19:54Z +++ I use [cask][] for managing the dependencies of my Emacs configuration. Whenever I opened my `Cask` file, I wondered if I really was using all the sources I had defined: {{< highlight cl >}} (source gnu) (source marmalade) (source melpa) (source melpa-stable) (source org) {{< /highlight >}} It seemed quite strange that we have so many package repositories in the Emacs world and I'm not even using all of them. I find this state less than ideal, much as [Jorgen Schäfer details][state of emacs package archives]. My ideal package repository would be once that works with VCS releases, mostly because it's a much simpler process to work with than having to sign up to yet another website just to upload a package, then ensure it's kept up-to-date on every release. As such, I prefer the concepts behing [MELPA][] and [MELPA Stable][] to those of [Marmalade][]. [GNU ELPA][] doesn't appear to allow any submissions and [org][org archive] is specific to [org-mode]. I've also noticed that many packages I find and use are on github and so work with the [MELPA][] system. However, I don't like [MELPA's][MELPA] versioning: it just gets the latest code and puts the build date in the version, meaning that packages could break at any time. So, ideally I would use [MELPA Stable][] as much as possible and reduce my usage of [Marmalade][] and [MELPA][]. [GNU ELPA][] doesn't appear to have many packages, but I wasn't sure if I was using any. I couldn't see the information listed in the `*Packages*` buffer, so I decided to try to figure out how to generate some usage statistics. I found [how to get a list of installed packages][], but that just gives a list: {{< highlight cl >}} (ace-jump-mode ag auto-compile auto-indent-mode autopair ...) {{< /highlight >}} I needed to get more information about those packages. I looked at where `list-packages` gets that information from. It seems that `package-archive-contents` is a list of cons cells: {{< highlight cl >}} (org-plus-contrib . [(20140714) nil "Outline-based notes management and organizer" tar "org"]) {{< /highlight >}} Then created a function to loop over the contents of `package-activated-list`, retrieving the corresponding contents of `package-archive-contents`: {{< highlight cl >}} (defun package-list-installed () (loop for pkg in package-activated-list collect (assq pkg package-archive-contents))) {{< /highlight >}} This generates a list of arrays from `package-archive-contents`. There are some helper functions in package.el such as `package-desc-kind`. `package-desc-archive` was exactly what I needed. I happened to be using a pretest version of Emacs at the time and didn't know that it's not in 24.3, so I just made sure it was defined: {{< highlight cl >}} (if (not (fboundp #'package-desc-archive)) (defsubst package-desc-archive (desc) (aref desc (1- (length desc))))) {{< /highlight >}} Weirdly, some of the arrays (seemingly the ones from the [org archive][]) had a different length, but the repository/archive was always the last element, which is why I used `(1- (length ))` and not a constant, like the other `package-desc-*` functions. To generate a list of statistics, I just needed to loop over the installed packages from `package-list-installed` and update a count for each archive: {{< highlight cl >}} (defun package-archive-stats () (let ((archives (makehash)) (assoc '())) (dolist (arc package-archives) (puthash (car arc) 0 archives)) (maphash (lambda (k v) (setq assoc (cons (cons k v) assoc))) (dolist (pkg (-filter #'identity (package-list-installed)) archives) (let ((pkg-arc (package-desc-archive (cdr pkg)))) (incf (gethash pkg-arc archives))))) assoc)) {{< /highlight >}} Running this gives a list of cons cells: {{< highlight cl >}} (("gnu" . 0) ("org" . 1) ("melpa-stable" . 2) ("melpa" . 106) ("marmalade" . 1)) {{< /highlight >}} I wrapped it in an interactive function so that I could check the numbers quickly: {{< highlight cl >}} (defun package-show-archive-stats () (interactive) (message "%s" (package-archive-stats))) {{< /highlight >}} With that, I removed `(source gnu)` from my `Cask` file. Now I had another question. What package was installed from [marmalade][]? In the lisp fashion, I created yet another function: {{< highlight cl >}} (defun package-show-installed-from-archive (archive) (interactive (list (helm-comp-read "Archive: " (mapcar #'car package-archives) :must-match t))) (let ((from-arc (mapcar #'car (--filter (equalp (package-desc-archive (cdr it)) archive) (package-list-installed))))) (if (called-interactively-p) (message "%s" from-arc) from-arc))) {{< /highlight >}} (Non-helm users can replace `helm-comp-read` with `ido-completing-read` or similar) Running this with the argument `"marmalade"` gives: {{< highlight cl >}} (php-extras) {{< /highlight >}} I checked on [MELPA Stable][] and [MELPA][], but it's not available there. Given that I use [php-extras][] quite a bit at work, I can't remove [marmalade][] just yet. However, as it's a git repository, it should be easy for me to create a recipe for MELPA. Then I can remove marmalade from my [cask][] configuration. Hooray for simplification! Hopefully, packaging in Emacs will become simpler in the future. There are some interesting things in 24.4 like pinning packages to a repository, which would allow [MELPA Stable][] to be used even when [MELPA][] defines the same package with a higher "version". [cask]: https://github.com/cask/cask/ [state of emacs package archives]: http://blog.jorgenschaefer.de/2014/06/the-sorry-state-of-emacs-lisp-package.html [marmalade]: http://marmalade-repo.org/ [GNU ELPA]: http://elpa.gnu.org/packages/ [MELPA]: http://hiddencameras.milkbox.net/ [MELPA Stable]: http://melpa-stable.milkbox.net/ [org archive]: http://orgmode.org/elpa.html [how to get a list of installed packages]: http://stackoverflow.com/questions/13866848/how-to-save-a-list-of-all-the-installed-packages-in-emacs-24 [php-extras]: https://github.com/arnested/php-extras [org-mode]: http://orgmode.org/