How to determine whether a package is installed in elisp?
Asked Answered
C

7

32

I want to customize environment while the specific package is installed properly. How to check whether some package is installed in elisp? Something like this?:

(if (require 'ecb)
    (progn (setq ....))
  (message "ECB not installed!"))
Celeriac answered 17/10, 2011 at 7:3 Comment(0)
T
42

tripleee's answer is a handy example of error handling, but unnecessary in this instance.

(when (require 'some-library nil 'noerror)
  do-things)

That 'noerror can be any non-nil value, but of course it's more descriptive this way. I often see :noerror used as well, but I've no idea if there's any particular advantage to using a keyword argument over a symbol (comments, anyone? I'm quite interested to know).

require is a built-in function in C source code.

(require FEATURE &optional FILENAME NOERROR)

If feature FEATURE is not loaded, load it from FILENAME.
If FEATURE is not a member of the list features, then the feature
is not loaded; so load the file FILENAME.
If FILENAME is omitted, the printname of FEATURE is used as the file name,
and load will try to load this name appended with the suffix .elc or
.el, in that order. The name without appended suffix will not be used.
See get-load-suffixes for the complete list of suffixes.
If the optional third argument NOERROR is non-nil,
then return nil if the file is not found instead of signaling an error.
Normally the return value is FEATURE.
The normal messages at start and end of loading FILENAME are suppressed.

Tanta answered 17/10, 2011 at 9:30 Comment(3)
For what it's worth, the check is only to see if 'noerror there is defined, so any non-nil value will work. I've been using t myself.Tamis
This is not working with AUCTeX because AUCTeX cannot be required. You need to use package-installed-p for AUCTeXDylandylana
auctex does seem a bit unusual. It looks like you could test for tex-site after loading auctex though. e.g. (load "auctex" :noerror) (when (require 'tex-site nil :noerror) ...)Tanta
H
11

Why not using "featurep"?

(featurep FEATURE &optional SUBFEATURE)

Return t if FEATURE is present in this Emacs.

(if (featurep 'ecb)
   (message "ECB is there!"))
Hildegaard answered 24/10, 2011 at 14:51 Comment(1)
This is only applicable to features which have already been loaded (and so is less relevant to this particular question).Tanta
H
8

The (require) will throw an error if it fails. That should really be all you need.

If you want to configure ECB's behavior only when it is available, look primarily into adding stuff to ecb-hook -- this is the normal way to configure an Emacs package conditionally.

If no hook is available, or you want to roll it by hand for some reason, try something like

(eval-after-load 'ecb '(setq ecb-be-more-like-better-yes-p t))

If you really really want to roll this by hand, you can trap the error from a failed require like this:

(condition-case nil
   (progn
      (require 'ecb)
      (setq ecb-be-more-like-better-yes-p t) )
   (file-error (message "ECB not available; not configuring") ))

Note that the condition-case will catch any file-error from inside the progn so you want to make sure you don't do any other file operations inside. Ultimately you may want to put just the require inside a condition-case and use that as the condition for your original if form, but this is already getting out of hand ...

(if (condition-case nil (require 'ecb) (error nil))
   (setq ecb-be-more-like-better-yes-p t)
 (message "ECB not available; not configuring") )
Hume answered 17/10, 2011 at 7:48 Comment(2)
eval-after-load seems not load the package. So I changed to the answer below: (when (...))Celeriac
Yeah, the purpose of eval-after-load is to specify a deferred action to execute if and when you do load the specified package. It's useful for avoiding unnecessary setup when you launch Emacs for a task which does not involve loading ecb (batch byte compilation is a perennial example).Hume
A
8

For people wondering how to check if a package.el package is installed, use package-installed-p.

And answered 21/1, 2017 at 3:45 Comment(0)
M
2

The simplest answer is to use require and eval-after-load as stated in other answers.

However that is not always convenient, such as in a function called by a mode hook that wants to activate another minor mode but only if the package for it is installed. In this case featurep is relevant.

Emacs packages increasingly use autoload to improve startup time. If you test for the presence of a package by using require then you are wearing the cost of loading the file(s). If using ELPA/MELPA/Marmalade packages (available by default since version 24) then many packages may be available in an as-yet unloaded state, but a package foo with autoloads will provide a feature foo-autoloads. I wrote a function that is useful for testing whether a package is available in terms of already being loaded or set to autoload.

(defun autofeaturep (feature)
  "For a feature symbol 'foo, return a result equivalent to:
(or (featurep 'foo-autoloads) (featurep 'foo))
Does not support subfeatures."
  (catch 'result
    (let ((feature-name (symbol-name feature)))
      (unless (string-match "-autoloads$" feature-name)
        (let ((feature-autoloads (intern-soft (concat feature-name "-autoloads"))))
          (when (and feature-autoloads (featurep feature-autoloads))
            (throw 'result t))))
      (featurep feature))))
Miles answered 25/9, 2012 at 4:15 Comment(0)
D
1

Four years late on this question, but... here's a simple macro that will do this for you.

(defmacro when-feature-loaded (feature &rest body)
  "Executes BODY if and only if FEATURE is loaded."
  (declare (indent defun))
  `(when (featurep ,module)
    @,body))

For example:

(when-feature-loaded 'foo
  (message "foo is loaded!"))

Here's another version with an "else" case, in case you need to handle that as well.

(defmacro if-feature-loaded (module then-form else-form)
  "Executes THEN-FORM if and only if MODULE is already loaded, otherwise executes ELSE-FORM."
  (declare (indent 2))
  `(if (featurep ,module)
       ,then-form
     ,else-form))
Dislodge answered 21/8, 2016 at 0:19 Comment(2)
this does not need to be a macroClaude
Does this compile as-is? Looks like it needs a module -> feature in the first snippet.Infective
I
1

N.B.: Using (require ..) will load the package, which defeats the benefit of autoloads (deferred loading).

I use this in my code to configure functions which are autoloaded:

(when (fboundp 'some-function)
  ...)

Example

I have this in my init.el:

(when (fboundp 'ace-select-window)
  (keymap-global-set "M-s-u" 'ace-select-window))

If you have ace-window installed, you can test this yourself by copying it into your own init file, restarting Emacs, and running:

(autoloadp (symbol-function 'ace-select-window))

It should return T. Then run the command, and execute that snippet again; it should return nil.

(newbie tip: Hit altshift:, then enter that, and press enter)

This avoids loading your package just to configure it (which is the point of autoloading).

(newbie tip nr 2: using fbound is for functions; use boundp for values)

Infective answered 6/1, 2023 at 17:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.