开发者

How to determine whether a package is installed in elisp?

开发者 https://www.devze.com 2023-04-13 06:03 出处:网络
I want to customize environment while the specific package is installed properly. How to check whether some package is installe开发者_如何学Cd in elisp?

I want to customize environment while the specific package is installed properly. How to check whether some package is installe开发者_如何学Cd in elisp? Something like this?:

(if (require 'ecb)
    (progn (setq ....))
  (message "ECB not installed!"))


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.


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") )


Why not using "featurep"?

(featurep FEATURE &optional SUBFEATURE)

Return t if FEATURE is present in this Emacs.

(if (featurep 'ecb)
   (message "ECB is there!"))


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


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))


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))))


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)

0

精彩评论

暂无评论...
验证码 换一张
取 消