How to manage Emacs packages effectively
Here are a few techniques I developed after reading Steve Purcell's setup.
The techniques are compatible with use-package because it uses Emacs API.
Mid-level Lisp knowledge is required to read this article.
Do NOT use package.el for certain packages
Create the directory
~/.emacs.d/site-lisp. Then insert below code into
(if (fboundp 'normal-top-level-add-to-load-path) (let* ((my-lisp-dir "~/.emacs.d/site-lisp/") (default-directory my-lisp-dir)) (progn (setq load-path (append (loop for dir in (directory-files my-lisp-dir) unless (string-match "^\\." dir) collecting (expand-file-name dir)) load-path)))))
You can place a package's source code at sub-directory of
~/.emacs.d/site-lisp/. That's all you need to do to install packages.
Create your own package repository
Step 1, Place two files "archive-contents" and "hello-1.0.0.el" in any directory. Say
Here is the content of
(1 (hello . [(1 0 0) nil "Say hello" single]) )
Here is the content of
;;;###autoload (defun hello-say () (interactive) (message "Hi, hello!")) (provide 'hello)
Step 2, insert below code into
(add-to-list 'package-archives '("localelpa" . "~/.emacs.d/localelpa"))
Step 3, restart Emacs and run
M-x list-packages. As you can see, you can install package named "hello" now!
Here is a real world example how I apply this technique. I use
rainbow-mode from https://elpa.gnu.org/ which shuts down sometimes. So I built a local repository to host
rainbow-mode and a few other packages to remove dependency on GNU site.
Orphan package issue is also resolved by
elpa-mirror. You can delete everything from
~/.emacs.d/elpa and set the repository to the local repository created by
elpa-mirror. It only takes 30 seconds to install 300 packages.
package--add-to-archive-contents to filter packages
Insert below code into ~/.emacs,
;; List of VISIBLE packages from melpa-unstable (http://melpa.org) ;; Feel free to add more packages! (defvar melpa-include-packages '(bbdb color-theme company-c-headers) "Don't install any mELPA packages except these packages") (defvar package-filter-function nil "Optional predicate function used to internally filter packages used by package.el. The function is called with the arguments PACKAGE VERSION ARCHIVE, where PACKAGE is a symbol, VERSION is a vector as produced by `version-to-list', and ARCHIVE is the string name of the package archive.") ;; Don't take MELPA versions of certain packages (setq package-filter-function (lambda (package version archive) (or (not (string-equal archive "melpa")) ;; install package in whitelist (memq package melpa-include-packages) ;; use all color themes (string-match (format "%s" package) "-theme")))) (defadvice package--add-to-archive-contents (around filter-packages (package archive) activate) "Add filtering of available packages using `package-filter-function', if non-nil." (when (or (null package-filter-function) (funcall package-filter-function (car package) (funcall (if (fboundp 'package-desc-version) 'package--ac-desc-version 'package-desc-vers) (cdr package)) archive)) ad-do-it))
The above code builds the filter defined in
package-filter-function to get the final version of packages list.
The filter accepts the package if it's NOT from melpa-unstable OR it's listed in
melpa-include-packages OR its name contains "-theme".
Surely you can build your own filter.
This solution is copied from Steve Purcell's setup with a little modification.
You can combine above techniques to solve any package issue.
For example, package A is dependent on package B. Both A and B have two versions, 1.0 and 2.0:
- A 2.0 can use B 1.0 and B 2.0, but A 1.0 can ONLY use B 1.0
- A 2.0 can ONLY use B 2.0, and A 1.0 can only use B 1.0
The solution is simple. We create a local repository to host B
1.0 and A
1.0. As I said,
package-filter-function only returns a boolean expression. So you can design any strategy.
I know some one believs "Emacs package manager sucks" after mastering it for seven years. That's certainly not the truth as I have proved. I spent 15 minutes to reach the opposite conclusion when I was a Emacs dummy.
It's possibly I started my journey by learning from experts instead of "studiyng" by myself.