From 641f304fb878ecfb0a1afe2d6e62281d0e3e94ab Mon Sep 17 00:00:00 2001 From: Alan Pearce Date: Sat, 19 Jul 2014 13:23:56 +0100 Subject: Write post about Emacs package archive statistics --- content/post/emacs-package-archive-statistics.md | 168 +++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 content/post/emacs-package-archive-statistics.md (limited to 'content') 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 -- cgit 1.4.1