emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

FIXED!! Re: Clarification of eval-after-load [was: Problem mit symlinks,


From: Alan Mackenzie
Subject: FIXED!! Re: Clarification of eval-after-load [was: Problem mit symlinks, locate-library and load-history]
Date: Sun, 14 May 2006 11:07:49 +0000 (GMT)

Hi, again, Emacs!

On Wed, 10 May 2006, Alan Mackenzie wrote:

>Hi, Emacs!

>The doc string (and manual entry) for eval-after-load are a bit hazy and
>indefinite.  Here is the doc-string:

>   "Arrange that, if FILE is ever loaded, FORM will be run at that time.
>    This makes or adds to an entry on `after-load-alist'.
>    If FILE is already loaded, evaluate FORM right now.
>    It does nothing if FORM is already on the list for FILE.
>    FILE must match exactly.  Normally FILE is the name of a library,
>    with no directory or extension specified, since that is how `load'
>    is normally called.
>    FILE can also be a feature (i.e. a symbol), in which case FORM is
>    evaluated whenever that feature is `provide'd."

>Some problems are:
>(i) "FILE must match exactly" doesn't say what FILE must match, or how it
>must match it, and it is unclear what "exactly" means.  Given this
>description, I would not expect the argument "font-lock" to match the
>file name "font-lock.elc", but it does.  :-(

>(ii) The doc-string doesn't say what happens when FILE gets loaded again.

Combining this with these OTHER PROBLEMS (already posted on emacs-devel):

> I do: emacs-22.0.50.1 -Q
>       M-x load-file <ret> ~acm/emacs/emacs/lisp/progmodes/cc-mode.elc
>       C-h v load-history.
>
> Then:
>       M-: (eval-after-load "cc-mode" '(beep))  fails.
>       M-: (eval-after-load "cc-fonts" '(beep))  beeps.
>       M-: (eval-after-load "english" '(beep))  fails.
>
> Here are these file names as they appear in load-history:
>
> 1. "/home/acm/emacs/emacs/lisp/progmodes/cc-mode.elc"
> 2. "/mnt/hda7/emacs/lisp/progmodes/cc-fonts.elc"
> 3. "/mnt/hda7/emacs/lisp/language/english.elc"
>
> 1 and 2 are incompatible.  3 is plain wrong - the actual file is
> english.el (english.elc doesn't exist; you can't compile english).

and

> Start Emacs 22 with -Q, then visit a file.c.
>
> Emacs displays the message:
>
>   File mode specification error: (void-variable c-font-lock-keywords-3)

#########################################################################

The following patch, though large, fixes all of these problems, thusly:

1. The file name recorded in load-history is the absolute true file name
of the file actually loaded.  The way this is done for preloaded files
has been simplified.

2. (eval-after-load FILE ...) now triggers on ANY file which matches
FILE, not just the one (if any) found in load-path.

3. (eval-after-load "foo" ...), for example will trigger on "..../foo",
"..../foo.el",  "..../foo.elc", or even "..../foo.el.gz".

[Incidentally, it needs BASE_PURESIZE to be increased.]



2006-05-14  Alan Mackenzie  <address@hidden>

        * startup.el (command-line): For names of preloaded files, don't
        append ".elc" (now done in Fload), and call file-truename on the
        lisp directory.

        * international/mule.el (load-with-code-conversion): Do the
        eval-after-load stuff by calling do-after-load-evaluation.

        * subr.el (eval-after-load): Fix the doc-string.  Allow FILE to
        match ANY loaded file with the right name, not just those in
        load-path.

        * subr.el: New functions load-history-regexp,
        load-history-filename-element, loaded-filename,
        get-after-load-alist-matches, do-after-load-evaluation.

        * loadup.el: load emacs-lisp/regexp-opt at preload time to support
        Fload.


Index: lisp/startup.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/startup.el,v
retrieving revision 1.407
diff -c -r1.407 startup.el
*** lisp/startup.el     5 May 2006 14:05:54 -0000       1.407
--- lisp/startup.el     14 May 2006 09:51:50 -0000
***************
*** 644,661 ****
  
    ;; Convert preloaded file names to absolute.
    (let ((lisp-dir
!        (file-name-directory
!         (locate-file "simple" load-path
!                      (get-load-suffixes)))))
  
      (setq load-history
          (mapcar (lambda (elt)
                    (if (and (stringp (car elt))
                             (not (file-name-absolute-p (car elt))))
                        (cons (concat lisp-dir
!                                     (car elt)
!                                     (if (string-match "[.]el$" (car elt))
!                                         "" ".elc"))
                              (cdr elt))
                      elt))
                  load-history)))
--- 644,660 ----
  
    ;; Convert preloaded file names to absolute.
    (let ((lisp-dir
!        (file-truename
!         (file-name-directory
!          (locate-file "simple" load-path
!                       (get-load-suffixes))))))
  
      (setq load-history
          (mapcar (lambda (elt)
                    (if (and (stringp (car elt))
                             (not (file-name-absolute-p (car elt))))
                        (cons (concat lisp-dir
!                                     (car elt))
                              (cdr elt))
                      elt))
                  load-history)))
Index: lisp/subr.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/subr.el,v
retrieving revision 1.507
diff -c -r1.507 subr.el
*** lisp/subr.el        7 May 2006 20:49:01 -0000       1.507
--- lisp/subr.el        14 May 2006 09:52:01 -0000
***************
*** 1385,1416 ****
                 t))
       nil))
  
  (defun eval-after-load (file form)
    "Arrange that, if FILE is ever loaded, FORM will be run at that time.
- This makes or adds to an entry on `after-load-alist'.
  If FILE is already loaded, evaluate FORM right now.
! It does nothing if FORM is already on the list for FILE.
! FILE must match exactly.  Normally FILE is the name of a library,
! with no directory or extension specified, since that is how `load'
! is normally called.
! FILE can also be a feature (i.e. a symbol), in which case FORM is
! evaluated whenever that feature is `provide'd."
    (let ((elt (assoc file after-load-alist)))
-     ;; Make sure there is an element for FILE.
      (unless elt (setq elt (list file)) (push elt after-load-alist))
!     ;; Add FORM to the element if it isn't there.
      (unless (member form (cdr elt))
!       (nconc elt (list form))
!       ;; If the file has been loaded already, run FORM right away.
!       (if (if (symbolp file)
!             (featurep file)
!           ;; Make sure `load-history' contains the files dumped with
!           ;; Emacs for the case that FILE is one of them.
!           ;; (load-symbol-file-load-history)
!           (when (locate-library file)
!             (assoc (locate-library file) load-history)))
!         (eval form))))
!   form)
  
  (defun eval-next-after-load (file)
    "Read the following input sexp, and run it whenever FILE is loaded.
--- 1385,1494 ----
                 t))
       nil))
  
+ (defun load-history-regexp (file)
+   "Form a regexp to find FILE in load-history.
+ FILE, a string, is described in eval-after-load's doc-string."
+   (if (file-name-absolute-p file)
+       (setq file (file-truename file)))
+   (concat (if (file-name-absolute-p file) "\\`" "\\<")
+         (regexp-quote file)
+         (if (file-name-extension file)
+             ""
+           (regexp-opt (cons "" load-suffixes)))
+         "\\(" (regexp-opt jka-compr-load-suffixes) "\\)?\\'"))
+ 
+ (defun load-history-filename-element (file)
+   "Get the first element of load-history which matches FILE.
+ Return nil if there isn't one.
+ 
+ FILE, a string, is described in eval-after-load's doc-string."
+   (let* ((regexp (load-history-regexp file))
+        (loads load-history)
+        (load-elt (and loads (car loads))))
+     (save-match-data
+       (while (and loads
+                 (or (null (car load-elt))
+                     (not (string-match regexp (car load-elt)))))
+       (setq loads (cdr loads)
+             load-elt (and loads (car loads)))))
+     load-elt))
+ 
+ (defun loaded-filename (file)
+   "Get the absolute file name from which Elisp file FILE was loaded.
+ Return nil if the file isn't found.  The returned file name will be a true
+ name \(i.e. with all its symbolic links having been chased out).
+ 
+ FILE is a string.  See eval-after-load for precise description."
+   (let ((elt (load-history-filename-element file)))
+     (and elt (car elt))))
+ 
  (defun eval-after-load (file form)
    "Arrange that, if FILE is ever loaded, FORM will be run at that time.
  If FILE is already loaded, evaluate FORM right now.
! 
! If a matching file is loaded again, FORM will be evaluated again.
! 
! If FILE is a string, it may be either an absolute or a relative file
! name, and may have an extension \(e.g. \".el\") or may lack one, and
! additionally may or may not have an extension denoting a compressed
! format \(e.g. \".gz\").
! 
! When FILE is absolute, it is first converted to a true name by chasing
! out symbolic links.  Only a file of this name \(see next paragraph for
! extensions) will trigger the evaluation of FORM.  When FILE is relative,
! a file whose absolute true name ends in FILE will trigger evaluation.
! 
! When FILE lacks an extension, a file name with any extension will trigger
! evaluation.  Otherwise, its extension must match FILE's.  A further
! extension for a compressed format \(e.g. \".gz\") on FILE will not affect
! this name matching.
! 
! Alternatively, FILE can be a feature (i.e. a symbol), in which case FORM
! is evaluated whenever that feature is `provide'd.
! 
! Usually FILE is just a library name like \"font-lock\" or a feature name
! like 'font-lock.
! 
! This function makes or adds to an entry on `after-load-alist'."
!   ;; Add this FORM into after-load-alist (regardless of whether we'll be
!   ;; evaluating it now).
    (let ((elt (assoc file after-load-alist)))
      (unless elt (setq elt (list file)) (push elt after-load-alist))
!     ;; Add FORM to the element unless it's already there.
      (unless (member form (cdr elt))
!       (nconc elt (list form))))
! 
!   ;; Is there an already loaded file whose name (or `provide' name)
!   ;; matches FILE?
!   (if (if (symbolp file)
!         (featurep file)
!       (loaded-filename file))
!       (eval form)))
! 
! (defun get-after-load-alist-matches (abs-file)
!   "Get all the matches in after-load-alist for the file name ABS-FILE.
! ABS-FILE, a string, should be the absolute true name of a file just loaded."
!   (let ((elements after-load-alist)
!       element result)
!     (while elements
!       (setq element (car elements))
!       (if (and (stringp (car element))
!              (string-match (load-history-regexp (car element)) abs-file))
!         (push element result))
!       (setq elements (cdr elements)))
!     (nreverse result)))
! 
! (defun do-after-load-evaluation (abs-file)
!   "Evaluate all `eval-after-load' forms, if any, for ABS-FILE.
! ABS-FILE, a string, should be the absolute true name of a file just loaded."
!   (let ((file-elements (get-after-load-alist-matches abs-file))
!       file-element form)
!     (while file-elements
!       (setq file-element (car file-elements)
!           file-elements (cdr file-elements))
!       (while (setq file-element (cdr file-element)) ; discard the file name.
!       (setq form (car file-element))
!       (eval form)))))
  
  (defun eval-next-after-load (file)
    "Read the following input sexp, and run it whenever FILE is loaded.
Index: lisp/loadup.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/loadup.el,v
retrieving revision 1.148
diff -c -r1.148 loadup.el
*** lisp/loadup.el      20 Feb 2006 22:14:54 -0000      1.148
--- lisp/loadup.el      14 May 2006 09:52:02 -0000
***************
*** 211,216 ****
--- 211,217 ----
  (load "vc-hooks")
  (load "jka-cmpr-hook")
  (load "ediff-hook")
+ (load "emacs-lisp/regexp-opt")                ; needed for Fload.
  (if (fboundp 'x-show-tip) (load "tooltip"))
  
  (message "%s" (garbage-collect))
Index: lisp/international/mule.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/international/mule.el,v
retrieving revision 1.234
diff -c -r1.234 mule.el
*** lisp/international/mule.el  21 Apr 2006 12:22:24 -0000      1.234
--- lisp/international/mule.el  14 May 2006 09:52:08 -0000
***************
*** 98,106 ****
                         ))
        (let (kill-buffer-hook kill-buffer-query-functions)
          (kill-buffer buffer)))
!       (let ((hook (assoc file after-load-alist)))
!       (when hook
!         (mapcar (function eval) (cdr hook))))
        (unless (or nomessage noninteractive)
        (if source
            (message "Loading %s (source)...done" file)
--- 98,105 ----
                         ))
        (let (kill-buffer-hook kill-buffer-query-functions)
          (kill-buffer buffer)))
!       (do-after-load-evaluation fullname)
!       
        (unless (or nomessage noninteractive)
        (if source
            (message "Loading %s (source)...done" file)




2006-05-14  Alan Mackenzie  <address@hidden>

        * lread.c (Vload_history): Enhance doc-string to say that the file
        is the absolute truename of the loaded file.

        * lread.c (readevalloop): Call file-truename on the name for
        load-history, except at preloading time.

        * lread.c (Fload): At preloading time, preserve the extension of
        the filename which goes into load-history.  New variable
        hist_file_name.

        * lread.c (Fload): Do eval-after-load stuff by calling the lisp
        function do-after-load-evaluation.
        

Index: src/lread.c
===================================================================
RCS file: /cvsroot/emacs/emacs/src/lread.c,v
retrieving revision 1.350
diff -c -r1.350 lread.c
*** src/lread.c 27 Feb 2006 02:04:35 -0000      1.350
--- src/lread.c 14 May 2006 09:51:44 -0000
***************
*** 718,725 ****
    register int fd = -1;
    int count = SPECPDL_INDEX ();
    Lisp_Object temp;
!   struct gcpro gcpro1, gcpro2;
!   Lisp_Object found, efound;
    /* 1 means we printed the ".el is newer" message.  */
    int newer = 0;
    /* 1 means we are loading a compiled file.  */
--- 718,725 ----
    register int fd = -1;
    int count = SPECPDL_INDEX ();
    Lisp_Object temp;
!   struct gcpro gcpro1, gcpro2, gcpro3;
!   Lisp_Object found, efound, hist_file_name;
    /* 1 means we printed the ".el is newer" message.  */
    int newer = 0;
    /* 1 means we are loading a compiled file.  */
***************
*** 727,732 ****
--- 727,733 ----
    Lisp_Object handler;
    int safe_p = 1;
    char *fmode = "r";
+   Lisp_Object tmp[2];
  #ifdef DOS_NT
    fmode = "rt";
  #endif /* DOS_NT */
***************
*** 743,749 ****
       the need to gcpro noerror, nomessage and nosuffix.
       (Below here, we care only whether they are nil or not.)
       The presence of this call is the result of a historical accident:
!      it used to be in every file-operations and when it got removed
       everywhere, it accidentally stayed here.  Since then, enough people
       supposedly have things like (load "$PROJECT/foo.el") in their .emacs
       that it seemed risky to remove.  */
--- 744,750 ----
       the need to gcpro noerror, nomessage and nosuffix.
       (Below here, we care only whether they are nil or not.)
       The presence of this call is the result of a historical accident:
!      it used to be in every file-operation and when it got removed
       everywhere, it accidentally stayed here.  Since then, enough people
       supposedly have things like (load "$PROJECT/foo.el") in their .emacs
       that it seemed risky to remove.  */
***************
*** 763,769 ****
    if (SCHARS (file) > 0)
      {
        int size = SBYTES (file);
-       Lisp_Object tmp[2];
  
        found = Qnil;
        GCPRO2 (file, found);
--- 764,769 ----
***************
*** 847,852 ****
--- 847,859 ----
      Vloads_in_progress = Fcons (found, Vloads_in_progress);
    }
  
+   /* Get the name for load-history. */
+   hist_file_name = (! NILP (Vpurify_flag)
+                     ? Fconcat (2, (tmp[0] = Ffile_name_directory (file),
+                                    tmp[1] = Ffile_name_nondirectory (found),
+                                    tmp))
+                     : found) ;
+ 
    if (!bcmp (SDATA (found) + SBYTES (found) - 4,
             ".elc", 4))
      /* Load .elc files directly, but not when they are
***************
*** 857,863 ****
          struct stat s1, s2;
          int result;
  
!         GCPRO2 (file, found);
  
          if (!safe_to_load_p (fd))
            {
--- 864,870 ----
          struct stat s1, s2;
          int result;
  
!         GCPRO3 (file, found, hist_file_name);
  
          if (!safe_to_load_p (fd))
            {
***************
*** 911,924 ****
  
          if (fd >= 0)
            emacs_close (fd);
!         val = call4 (Vload_source_file_function, found, file,
                       NILP (noerror) ? Qnil : Qt,
                       NILP (nomessage) ? Qnil : Qt);
          return unbind_to (count, val);
        }
      }
  
!   GCPRO2 (file, found);
  
  #ifdef WINDOWSNT
    emacs_close (fd);
--- 918,931 ----
  
          if (fd >= 0)
            emacs_close (fd);
!         val = call4 (Vload_source_file_function, found, hist_file_name,
                       NILP (noerror) ? Qnil : Qt,
                       NILP (nomessage) ? Qnil : Qt);
          return unbind_to (count, val);
        }
      }
  
!   GCPRO3 (file, found, hist_file_name);
  
  #ifdef WINDOWSNT
    emacs_close (fd);
***************
*** 957,970 ****
    load_descriptor_list
      = Fcons (make_number (fileno (stream)), load_descriptor_list);
    load_in_progress++;
!   readevalloop (Qget_file_char, stream, (! NILP (Vpurify_flag) ? file : 
found),
                Feval, 0, Qnil, Qnil, Qnil, Qnil);
    unbind_to (count, Qnil);
  
!   /* Run any load-hooks for this file.  */
!   temp = Fassoc (file, Vafter_load_alist);
!   if (!NILP (temp))
!     Fprogn (Fcdr (temp));
    UNGCPRO;
  
    if (saved_doc_string)
--- 964,986 ----
    load_descriptor_list
      = Fcons (make_number (fileno (stream)), load_descriptor_list);
    load_in_progress++;
!   readevalloop (Qget_file_char, stream, hist_file_name,
                Feval, 0, Qnil, Qnil, Qnil, Qnil);
    unbind_to (count, Qnil);
  
!   /* Run any eval-after-load forms for this file */
!   if (NILP (Vpurify_flag))
!   {
!     Lisp_Object Qdo_after_load_evaluation
!       = intern ("do-after-load-evaluation") ;
!     if (!NILP (Ffboundp (Qdo_after_load_evaluation)))
!       call1 (Qdo_after_load_evaluation, hist_file_name) ;
!      else
!        Fsignal (Qerror,
!                 Fcons (make_string
!                        ("Fload: can't find do-after-load-evaluation",
!                         42), Qnil)) ;
!   }
    UNGCPRO;
  
    if (saved_doc_string)
***************
*** 1385,1390 ****
--- 1401,1414 ----
  
    GCPRO4 (sourcename, readfun, start, end);
  
+   /* Try to ensure sourcename is a truename, except whilst preloading. */
+   if (NILP (Vpurify_flag) && !NILP (sourcename)
+       && Ffile_name_absolute_p (sourcename))
+     {
+       Lisp_Object Qfile_truename = intern ("file-truename") ;
+       if (!NILP (Ffboundp (Qfile_truename)))
+         sourcename = call1 (Qfile_truename, sourcename) ;
+     }
    LOADHIST_ATTACH (sourcename);
  
    continue_reading_p = 1;
***************
*** 3982,3987 ****
--- 4006,4015 ----
  Each alist element is a list that starts with a file name,
  except for one element (optional) that starts with nil and describes
  definitions evaluated from buffers not visiting files.
+ 
+ The file name is absolute and is the true file name (i.e. it doesn't
+ contain symbolic links) of the loaded file.
+ 
  The remaining elements of each list are symbols defined as variables
  and cons cells of the form `(provide . FEATURE)', `(require . FEATURE)',
  `(defun . FUNCTION)', `(autoload . SYMBOL)', and `(t . SYMBOL)'.

Index: src/puresize.h
===================================================================
RCS file: /cvsroot/emacs/emacs/src/puresize.h,v
retrieving revision 1.86
diff -c -r1.86 puresize.h
*** src/puresize.h      22 Apr 2006 10:09:36 -0000      1.86
--- src/puresize.h      14 May 2006 09:51:44 -0000
***************
*** 43,49 ****
  #endif
  
  #ifndef BASE_PURESIZE
! #define BASE_PURESIZE (1205000 + SYSTEM_PURESIZE_EXTRA + 
SITELOAD_PURESIZE_EXTRA)
  #endif
  
  /* Increase BASE_PURESIZE by a ratio depending on the machine's word size.  */
--- 43,51 ----
  #endif
  
  #ifndef BASE_PURESIZE
! #define BASE_PURESIZE (1210000 + SYSTEM_PURESIZE_EXTRA + 
SITELOAD_PURESIZE_EXTRA) 
! /* was (1205000 + SYSTEM_PURESIZE_EXTRA + SITELOAD_PURESIZE_EXTRA).  Increased
!    for regexp-opt, ACM, 2006/5/13 */
  #endif
  
  /* Increase BASE_PURESIZE by a ratio depending on the machine's word size.  */




-- 
>Alan.






reply via email to

[Prev in Thread] Current Thread [Next in Thread]