From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.devel 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) Message-ID: References: Reply-To: Alan Mackenzie NNTP-Posting-Host: main.gmane.org Mime-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII X-Trace: sea.gmane.org 1147604610 32396 80.91.229.2 (14 May 2006 11:03:30 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Sun, 14 May 2006 11:03:30 +0000 (UTC) Cc: Richard Stallman Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sun May 14 13:03:22 2006 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by ciao.gmane.org with esmtp (Exim 4.43) id 1FfENT-0007xk-Ii for ged-emacs-devel@m.gmane.org; Sun, 14 May 2006 13:03:16 +0200 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1FfENS-0004Ro-7R for ged-emacs-devel@m.gmane.org; Sun, 14 May 2006 07:03:10 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1FfENG-0004RA-EZ for emacs-devel@gnu.org; Sun, 14 May 2006 07:02:58 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1FfEND-0004QZ-9M for emacs-devel@gnu.org; Sun, 14 May 2006 07:02:58 -0400 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1FfEND-0004QW-4D for emacs-devel@gnu.org; Sun, 14 May 2006 07:02:55 -0400 Original-Received: from [193.149.49.134] (helo=acm.acm) by monty-python.gnu.org with esmtp (Exim 4.52) id 1FfEPI-0006Y8-IU; Sun, 14 May 2006 07:05:08 -0400 Original-Received: from localhost (root@localhost) by acm.acm (8.8.8/8.8.8) with SMTP id LAA00833; Sun, 14 May 2006 11:07:51 GMT X-Sender: root@acm.acm Original-To: emacs-devel@gnu.org In-Reply-To: X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:54435 Archived-At: 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 ~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 * 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 * 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.