Subject: Re: bug#19390: 25.0.50; `package-activate' is too slow

From: Dmitry Gutov <[email-masked]>
Date: Thu, 18 Dec 2014 02:14:22 +0200

Artur Malabarba <[email-masked]> writes:

> This will fail if the previously loaded version wasn't installed in the
> package-user-dir,  which, again, is the case for built-in packages. This
> also includes manually installed packages, which we may or may not care
> about supporting.

Good point, thanks. Here's the patch without the initial filtering part.
Still an improvement over the current code (0.6s vs 2.1s on my machine).

(And it drops the use of `file-name-sans-extension').

We could also check the `provide' value in each entry's alist, to make
sure of the match, probably at no major cost.

diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index 60beebd..90bb514 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -559,32 +559,26 @@ Return the max version (as a string) if the package is held at a lower version."
   "Recursively list all files in DIR which correspond to loaded features.
 Returns the `file-name-sans-extension' of each file, relative to
 DIR, sorted by most recently loaded last."
-  (let* ((history (mapcar (lambda (x) (file-name-sans-extension (car x)))
-                    load-history))
-         (dir (file-truename dir))
-         ;; List all files that have already been loaded.
-         (list-of-conflicts
-          (remove
-           nil
-           (mapcar
-               (lambda (x) (let* ((file (file-relative-name x dir))
-                             ;; Previously loaded file, if any.
-                             (previous
-                              (ignore-errors
-                                (file-name-sans-extension
-                                 (file-truename (find-library-name file)))))
-                             (pos (when previous (member previous history))))
-                        ;; Return (RELATIVE-FILENAME . HISTORY-POSITION)
-                        (when pos
-                          (cons (file-name-sans-extension file) (length pos)))))
-             (directory-files-recursively dir "\\`[^\\.].*\\.el\\'")))))
-    ;; Turn the list of (FILENAME . POS) back into a list of features.  Files in
-    ;; subdirectories are returned relative to DIR (so not actually features).
-    (let ((default-directory (file-name-as-directory dir)))
-      (mapcar (lambda (x) (file-truename (car x)))
-        (sort list-of-conflicts
-              ;; Sort the files by ascending HISTORY-POSITION.
-              (lambda (x y) (< (cdr x) (cdr y))))))))
+  (let* ((files (directory-files-recursively dir "\\`[^\\.].*\\.el\\'"))
+         (files-re (concat "/"
+                           (regexp-opt
+                            (mapcar (lambda (x)
+                                      (file-relative-name x dir))
+                                    files)
+                            t)
+                           "c?\\'"))
+         list-of-conflicts)
+    ;; List all the matching files from the load history, in
+    ;; historical order.
+    (dolist (entry load-history)
+      (let ((file (car entry)))
+        (when (string-match files-re file)
+          (cl-pushnew (substring (match-string 1 file) 0 -3)
+                      list-of-conflicts
+                      :test #'equal))))
+    ;; Files in subdirectories are returned relative to DIR (so not
+    ;; actually features).
+    list-of-conflicts))
 
 (defun package-built-in-p (package &optional min-version)
   "Return true if PACKAGE is built-in to Emacs.