Function: loaddefs-generate

loaddefs-generate is an autoloaded and byte-compiled function defined in loaddefs-gen.el.gz.

Signature

(loaddefs-generate DIRS OUTPUT-FILE &optional EXCLUDED-FILES EXTRA-DATA INCLUDE-PACKAGE-VERSION GENERATE-FULL)

Documentation

Generate loaddefs files for Lisp files in directories given by DIRS.

DIRS can be either a single directory or a list of directories.

The autoloads will be written to OUTPUT-FILE. If any Lisp file binds generated-autoload-file as a file-local variable, write its autoloads into the specified file instead.

This function does NOT recursively descend into subdirectories of the directories specified by DIRS.

Optional argument EXCLUDED-FILES, if non-nil, should be a list of files, such as preloaded files, whose autoloads should not be written to OUTPUT-FILE.

If EXTRA-DATA is non-nil, it should be a string; include that string at the beginning of the generated file. This will also force the generation of OUTPUT-FILE even if there are no autoloads to put into that file.

If INCLUDE-PACKAGE-VERSION is non-nil, include package version data.

If GENERATE-FULL is non-nil, regenerate all the loaddefs files anew, instead of just updating them with the new/changed autoloads.

View in manual

Aliases

hload-path--make-directory-autoloads

Source Code

;; Defined in /usr/src/emacs/lisp/emacs-lisp/loaddefs-gen.el.gz
;;;###autoload
(defun loaddefs-generate (dirs output-file &optional excluded-files
                               extra-data include-package-version
                               generate-full)
  "Generate loaddefs files for Lisp files in directories given by DIRS.
DIRS can be either a single directory or a list of directories.

The autoloads will be written to OUTPUT-FILE.  If any Lisp file
binds `generated-autoload-file' as a file-local variable, write
its autoloads into the specified file instead.

This function does NOT recursively descend into subdirectories of the
directories specified by DIRS.

Optional argument EXCLUDED-FILES, if non-nil, should be a list of
files, such as preloaded files, whose autoloads should not be written
to OUTPUT-FILE.

If EXTRA-DATA is non-nil, it should be a string; include that string
at the beginning of the generated file.  This will also force the
generation of OUTPUT-FILE even if there are no autoloads to put into
that file.

If INCLUDE-PACKAGE-VERSION is non-nil, include package version data.

If GENERATE-FULL is non-nil, regenerate all the loaddefs files anew,
instead of just updating them with the new/changed autoloads."
  (let* ((files-re (let ((tmp nil))
		     (dolist (suf (get-load-suffixes))
                       ;; We don't use module-file-suffix below because
                       ;; we don't want to depend on whether Emacs was
                       ;; built with or without modules support, nor
                       ;; what is the suffix for the underlying OS.
		       (unless (string-match "\\.\\(elc\\|so\\|dll\\|dylib\\)" suf)
                         (push suf tmp)))
                     (concat "\\`[^=.].*" (regexp-opt tmp t) "\\'")))
	 (files (apply #'nconc
		       (mapcar (lambda (d)
				 (directory-files (expand-file-name d)
                                                  t files-re))
			       (ensure-list dirs))))
         (updating (and (file-exists-p output-file) (not generate-full)))
         (defs nil))
    ;; Allow the excluded files to be relative.
    ;; We used to do (expand-file-name file dirs), which strangely enough
    ;; doesn't signal an error when DIRS is a list but does something weird
    ;; instead, so let's preserve the old behavior when DIRS is a string,
    ;; even tho it's different from what we do when it's a list.
    (let ((basedir (if (stringp dirs) dirs)))
      (setq excluded-files
            (mapcar (lambda (file) (expand-file-name file basedir))
                    excluded-files)))

    ;; Collect all the autoload data.
    (let ((progress (make-progress-reporter
                     (byte-compile-info
                      (format "Scraping %s files for loaddefs"
                              (length files)))
                     0 (length files) nil 10))
          (output-time
           (file-attribute-modification-time (file-attributes output-file)))
          (file-count 0))
      (dolist (file files)
        (progress-reporter-update progress (setq file-count (1+ file-count)))
        (when (or (not updating)
                  (time-less-p output-time
                               (file-attribute-modification-time
                                (file-attributes file))))
          ;; If we're scanning for package versions, we want to look
          ;; at the file even if it's excluded.
          (let* ((excluded (member file excluded-files))
                 (package-data
                  (and include-package-version (if excluded 'only t))))
            (when (or package-data (not excluded))
              (setq defs (nconc (loaddefs-generate--parse-file
                                 file output-file package-data)
                                defs))))))
      (progress-reporter-done progress))

    ;; First group per output file.
    (dolist (fdefs (seq-group-by (lambda (x) (expand-file-name (car x)))
                                 defs))
      (let ((loaddefs-file (car fdefs))
            hash)
        (with-temp-buffer
          (if (and updating (file-exists-p loaddefs-file))
              (insert-file-contents loaddefs-file)
            (insert (loaddefs-generate--rubric
                     loaddefs-file nil t include-package-version))
            (search-backward "\f")
            (when extra-data
              (insert extra-data)
              (ensure-empty-lines 1)))
          (setq hash (buffer-hash))
          ;; Then group by source file (and sort alphabetically).
          (dolist (section (sort (seq-group-by #'cadr (cdr fdefs))
                                 (lambda (e1 e2)
                                   (string<
                                    (file-name-sans-extension
                                     (file-name-nondirectory (car e1)))
                                    (file-name-sans-extension
                                     (file-name-nondirectory (car e2)))))))
            (pop section)
            (let* ((relfile (file-relative-name
                             (cadar section)
                             (file-name-directory loaddefs-file)))
                   (head (concat "\n\f\n;;; Generated autoloads from "
                                 relfile "\n\n")))
              (when (file-exists-p loaddefs-file)
                ;; If we're updating an old loaddefs file, then see if
                ;; there's a section here for this file already.
                (goto-char (point-min))
                (if (not (search-forward head nil t))
                    ;; It's a new file; put the data at the end.
                    (progn
                      (goto-char (point-max))
                      (search-backward "\f\n" nil t))
                  ;; Delete the old version of the section.  Strictly
                  ;; speaking this should search for "\n\f\n;;;", but
                  ;; there are loaddefs files in the wild that only
                  ;; have two ';;'.  (Bug#63236)
                  (delete-region (match-beginning 0)
                                 (and (search-forward "\n\f\n;;")
                                      (match-beginning 0)))
                  (forward-line -2)))
              (insert head)
              (dolist (def (reverse section))
                (setq def (caddr def))
                (if (stringp def)
                    (princ def (current-buffer))
                  (loaddefs-generate--print-form def))
                (unless (bolp)
                  (insert "\n")))))
          ;; Only write the file if we actually made a change.
          (unless (equal (buffer-hash) hash)
            (write-region (point-min) (point-max) loaddefs-file nil 'silent)
            (byte-compile-info
             (file-relative-name loaddefs-file (car (ensure-list dirs)))
             t "GEN")))))

    ;; If processing files without any autoloads, the above loop will
    ;; not generate any files.  If the function was invoked with
    ;; EXTRA-DATA, we want to ensure that even if no autoloads were
    ;; found, that at least a file will have been generated containing
    ;; the contents of EXTRA-DATA:
    (when (and extra-data (not (file-exists-p output-file)))
      (with-temp-buffer
        (insert (loaddefs-generate--rubric output-file nil t))
        (search-backward "\f")
        (insert extra-data)
        (ensure-empty-lines 1)
        (write-region (point-min) (point-max) output-file nil 'silent)))))