Setting Up Efficient Package Management in Emacs

Package management can be a significant challenge for Emacs users. We often face issues with dependencies, package builds, updates, and maintaining a clean configuration. Let’s explore how to set up a robust package management system.

Common Package Management Challenges in Emacs

1. multiple package sources

Emacs only includes the GNU ELPA repository by default. This limits access to many useful packages. Thus if we want to install the package we found (guaranteed to happen if we use Emacs), we need to:

  • Add additional package repositories
  • Manage potential conflicts between sources
  • Handle different update cycles

2. Complex Package Dependencies

After starting using a great package, we need to maintain it by upgrading to apply bugfix or dependency management. But we MUST NOT do this by hand: manual package management can lead to:

  • Missing dependencies
  • Version conflicts
  • Difficult troubleshooting

3. Package Build Occurs for Each Installation

Package installation involves compilation, which can cause:

  • Confusing warning messages
  • Failed installations due to compilation errors
  • Interrupted installation process due to debugging prompts

Solution: Configure Package Management System in Modern-way

Now, let’s open ~/.emacs.d/init.el and add the configurations to solve the problem described above.

First: Configure Package Sources

;; Configure package sources and directory
(eval-and-compile
  (when (or load-file-name byte-compile-current-file)
    (setq user-emacs-directory 
          (expand-file-name 
           (file-name-directory 
            (or load-file-name byte-compile-current-file)))))

  ;; Set up package archives
  (customize-set-variable
   'package-archives '(("org" . "https://orgmode.org/elpa/")
                      ("melpa" . "https://melpa.org/packages/")
                      ("gnu" . "https://elpa.gnu.org/packages/")))
  (package-initialize)

This configuration:

  • Sets up the user’s Emacs directory correctly during compilation
  • Adds MELPA and Org repositories alongside GNU ELPA
  • Initializes the package system

Seceond: Install and Configure leaf

We’ll use leaf by conao3 as our primary package manager to use its modern features and declarative syntax:

;; Install leaf if not present
(unless (package-installed-p 'leaf)
  (package-refresh-contents)
  (package-install 'leaf))

(leaf leaf-keywords
  :doc "Configure leaf and its extensions"
  :url "https://github.com/conao3/leaf.el"
  :ensure t
  :init
  (leaf el-get
    :doc "Add el-get for additional package sources"
    :ensure t
    :custom ((el-get-notify-type . 'message)
             (el-get-git-shallow-clone . t)))
  (leaf hydra
    :ensure t)
  :config
  (leaf-keywords-init))

This setup:

  • Installs leaf automatically if it’s missing
  • Configures el-get for additional package sources
  • Sets up hydra for key binding management
  • Initializes leaf keywords for extended functionality

Third: Configure Build Process

Before setting up package management, let’s configure how Emacs handles package compilation:

(eval-and-compile
  (leaf *byte-compile
    :custom ((byte-compile-warnings . '(not free-vars docstrings lexical unresolved constants))
            (warning-suppress-types . '(comp))
            (debug-on-error . nil))))

This configuration enables the installation process smoother and cleaner by suppressesing non-critical compilation warnings during package installation.

Fourth: Add Package Management Utilities

(leaf package-utils
  :doc "Interactive package manager"
  :url "https://github.com/Silex/package-utils"
  :ensure t)

This gives us commands for:

  • package-utils-upgrade-all: Update all packages
  • package-utils-remove-obsolete: Clean up old packages
  • package-utils-list-upgrades: Check available updates

Benefits of This Setup

  1. Extended Package Sources

    • Access to MELPA’s extensive package collection
    • Additional sources through el-get
  2. Declarative Package Management

    • Clear dependency specifications
    • Easy to maintain and modify
    • Self-documenting configuration
  3. Streamlined Build Process

    • Clean compilation output
    • Fewer interruptions during installation
    • Better error handling
  4. Automated Maintenance

    • Easy package updates
    • Dependency resolution
    • Cleanup obsolete packages

The final init.el

As a result, we’ve got our init.el being configured as follows:

(eval-and-compile
  (when (or load-file-name byte-compile-current-file)
    (setq user-emacs-directory
          (expand-file-name
           (file-name-directory (or load-file-name byte-compile-current-file))))))

;; Initialize package manager for compile time
(eval-and-compile
  (customize-set-variable
   'package-archives '(("org"   . "https://orgmode.org/elpa/")
                       ("melpa" . "https://melpa.org/packages/")
                       ("gnu"   . "https://elpa.gnu.org/packages/")))
  (package-initialize)
  (unless (package-installed-p 'leaf)
    (package-refresh-contents)
    (package-install 'leaf))

  ;; Leaf keywords
  (leaf leaf-keywords
    :doc "Use leaf as a package manager"
    :url "https://github.com/conao3/leaf.el"
    :ensure t
    :init
    (leaf el-get
      :ensure t
      :custom
      (el-get-notify-type       . 'message)
      (el-get-git-shallow-clone . t))
    (leaf hydra :ensure t)
    :config
    (leaf-keywords-init)))

;; Compile
(eval-and-compile
  (leaf *byte-compile
    :custom
    (byte-compile-warnings  . '(not free-vars docstrings lexical unresolved constants))
    (warning-suppress-types . '(comp))
    (debug-on-error         . nil)))

;; Package Manager
(leaf package-utils
  :doc "Interactive package manager"
  :url "https://github.com/Silex/package-utils"
  :ensure t)

Next Steps

We should start version controlling because our Emacs setup will continue to grow. In the following article, we will install a package that will help us using Git command from Emacs.


comments powered by Disqus