LibraryDependencies

This page is about dependencies among Lisp libraries. Click the title above to see all pages that link here.

Variable load-history

Variable ‘load-history’ records lots of information about what libraries have been loaded in your session so far, what functions and variables they each define, and what other libraries they require. Try ‘C-h v load-history’ - you’ll get a very long list.

The information in ‘load-history’ differs between different versions of Emacs. At least Emacs 20 contains less information than Emacs 21. Emacs 21 on the other hand has a bug that results in missing entries in ‘load-history’. Hopefully you are using Emacs 22 or 23 by now.

Library loadhist.el

Standard library `loadhist.el’ uses the information in ‘load-history’ to let you UnloadFeature things that were added in a library.

It also provides functions that let you know which library loaded a given function (‘feature-symbols’), the features required by a given library (‘file-requires’), the (loaded) libraries that require a given library (‘file-dependents’), and the features provided by a given library (‘file-provides’).

In other words, it’s a gold mine of information about the files, features, functions, and variables that have been loaded – in particular, about their interrelations.

Library lib-requires.el

Library lib-requires.el extends some of the functionality of standard library `loadhist.el’. Its aim is to provide information about the libraries that are required by a given library, recursively. That is, it tells you all of the libraries that are ultimately required, so it is kind of a super-‘file-requires’.

These are the commands provided by lib-requires.el:

Here is the value returned by ‘lib-requires-tree’ for library ‘doremi-frm’:

  (("doremi" ("ring+" "ring") mwheel)
   "hexrgb"
   ("ring+" "ring")
   ("frame-fns" "avoid")
   ("frame-cmds" ("strings" "thingatpt" ("thingatpt+" "thingatpt"))
                 ("frame-fns" "avoid")
                 ("icomplete+" "icomplete"))
   ("faces+" "faces" "thingatpt" ("thingatpt+" "thingatpt")))

Notice here that some of the same libraries are required more than once. For instance, library ‘thingatpt’ is required by `thingatpt+’, by ‘faces’, and by ‘strings’. And since `thingatpt+’ is required by ‘strings’ and `faces+’, ‘thingatpt’ is ultimately required several times.

The usefulness of ‘lib-requires-tree’ is that it shows you dependency structure. But ‘lib-requires’ is more useful to just get the list of libraries ultimately required by a library. Here is what it returns for library ‘doremi-frm’ as argument:

  ("avoid" "doremi" "faces" "faces+" "frame-cmds" "frame-fns" 
           "hexrgb" "icomplete" "icomplete+" mwheel "ring" "ring+" 
           "strings" "thingatpt" "thingatpt+")

And here is the Lisp comment that is inserted by ‘insert-lib-requires-as-comment’ for library ‘doremi-frm’:

  ;;  Features that might be required by this library:
  ;;
  ;;    `avoid', `doremi', `faces', `faces+', `frame-cmds', `frame-fns',
  ;;    `hexrgb', `icomplete', `icomplete+', `mwheel', `ring', `ring+',
  ;;    `strings', `thingatpt', `thingatpt+'.
  ;;

Just as for standard library `loadhist.el’, library `lib-requires.el’ uses the current value of ‘load-history’ to determine library dependencies. However, its commands first try to load the library being interrogated, which should load all the libraries that it requires, updating ‘load-history’.

If a ‘require’d library fails to load, it will of course not be recorded in ‘load-history’. A soft ‘require’ is one that raises no error if the required library is not loaded (e.g. not found); a hard ‘require’ raises an error.

Soft require: (require 'frame-cmds nil t) – no error if not found
Hard require: (require 'ring+) – error if not found

Given the knowledge of a soft ‘require’ for library ‘frame-cmds’, and looking at the example of ‘lib-requires-tree’ for library ‘doremi-frm’, you can see that some of the libraries ‘require’d by ‘frame-cmds’ are not really required.

Notice from the examples above that both ‘libreq-requires-tree’ and ‘libreq-requires-list’ generally return strings as library names – these are the file names (sans extension) of the corresponding required features.

Notice too that in both examples above, feature ‘mwheel’ is not represented by a string, but by a symbol. What that means is that feature ‘mwheel’ was not in fact loaded (it was not found): it is required (by library ‘doremi’), but it was not loaded. Since no error was raised, we know that ‘mwheel’ is a soft ‘require’ of ‘doremi’. (Library ‘mwheel’ is available in Emacs21 but not in Emacs 20; a hard ‘require’ would make library ‘doremi’ fail to load in Emacs 20.)

Otherwise, for libraries that were successfully loaded, there is no way to tell whether a given ‘require’ is soft or hard. This is very unfortunate, because such information is important, but ‘load-history’ does not record such a distinction, so there is nothing that can be done in this regard.

See Also: AutomaticFileHeaders for use of library lib-requires.el in automatically inserting and updating a comment with library dependencies (via ‘libreq-insert-lib-requires-as-comment’).

Library elisp-depend.el

ElispDepend is similar in some ways to lib-requires.el. A difference is that elisp-depend.el considers also autoloaded definitions (not just loaded libraries) to be dependencies. It parses the code in the current buffer, using ‘symbol-file’ to determine the defining (possibly autoloaded) library for each symbol. Like lib-requires.el, you can use elisp-depend.el to insert a comment about libraries that might be needed, but you can also use it to insert appropriate ‘require’ and ‘autoload’ code.

Library elx.el

Library elx.el can extract all kinds of information from an elisp file. It is similar to standard library lisp-mnt.el. Both of these libraries do not require the library from which information is extracted to be loaded. lisp-mnt.el actually only extracts information from the library header. elx.el on the other hand also provides functions elx-required and elx-provided which extract dependency information using regular expressions. Therefor it can be used in situations where some of the required libraries are not actually available or when one does not want to load the libraries for performance or security reasons.

Comments

Drew,

Cool set of libraries, not to mention wonderful explanatory text. I use a few macros that I find helps me with feature management. They don’t seem big enough to warrant a package of their own but maybe they fit in your libraries. No fuss if you don’t think so.

Also, this page is very formal with high quality. So, whatever you decide, please delete my comment. Move them to another page, delete them, incorporate them, or whatever. Just don’t let my ramblings mess up your nice page.

    (defmacro when-feature-loaded (feature &rest body)
      "When FEATURE is loaded, evaluate and execute BODY."
      `(when (featurep ,feature) ,@body))

    (defmacro unless-feature-loaded (feature &rest body)
      "Unless FEATURE is loaded, evaluate and execute BODY."
      `(unless (featurep ,feature) ,@body))

    (defmacro when-feature-loadable (feature &rest body)
      "If FEATURE has been or can be loaded, evaluate and execute BODY."
      `(when (require ,feature nil t) ,@body))

    (defmacro unless-feature-loadable (feature &rest body)
      "If cannot be loaded, evaluate and execute BODY."
      `(unless (require ,feature nil t) ,@body))

I made them macros under the, perhaps mistaken, belief that this may prevent some errors on edge cases. Specifically, if the FEATURE is not loaded and defines new syntax (eg: macros, syntax table modifications, hyperlinks, etc..) then the reading of BODY may generate an error. I haven’t really tested to see if that is the case. But the macro’s are only one-liners so I think it is more effort to determine that macros don’t help than to leave them as macros. – NeilSmithline 05-Jun-2011 at 20:57 ET

Hi Neil – Thanks for the feedback. My personal preference is actually to use the code that you have in the bodies of your macros (!), because I think it is clearer (clear without extra lookup, and about the same length). But I wouldn’t delete your comments and macros, if I were you, because I think others might find them useful. – DrewAdams


CategoryCode