summary refs log tree commit diff stats
path: root/content/post
diff options
context:
space:
mode:
Diffstat (limited to 'content/post')
-rw-r--r--content/post/emacs-package-archive-statistics.md168
1 files changed, 168 insertions, 0 deletions
diff --git a/content/post/emacs-package-archive-statistics.md b/content/post/emacs-package-archive-statistics.md
new file mode 100644
index 0000000..b610011
--- /dev/null
+++ b/content/post/emacs-package-archive-statistics.md
@@ -0,0 +1,168 @@
++++
+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