elinstall-do-segment distinguishes cases
[elinstall.git] / elinstall.el
blobca075313429c88a5ea4e74628065e57fb4796688
1 ;;;_ elinstall.el --- Automatically and flexibly install elisp files
3 ;;;_. Headers
4 ;;;_ , License
5 ;; Copyright (C) 2010 Tom Breton (Tehom)
7 ;; Author: Tom Breton (Tehom) <tehom@panix.com>
8 ;; Keywords: maint, tools, internal
10 ;; This file is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
15 ;; This file is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to
22 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
25 ;;;_ , Commentary:
27 ;; Entry points:
28 ;; elinstall Use this for overall loading
30 ;; elinstall-link-on-emacs-start - Use this for non-autogenerated
31 ;; files that need to be linked in.
33 ;; elinstall-update-directory-autoloads
34 ;; elinstall-update-file-autoloads
36 ;;;_ , Requires
38 (require 'autoload)
39 (require 'pp)
40 (require 'cus-edit) ;;Because we save "installedness" manually
43 ;;;_. Body
44 ;;;_ , Customizations
45 (defgroup elinstall
46 '()
47 "Customizations for elinstall"
48 :group 'elinstall)
50 (defcustom elinstall-default-priority
52 "Default priority for site-start"
53 :group 'elinstall
54 :type 'integer)
56 (defcustom elinstall-default-preload-target
57 "~/.emacs.d/site-start.d/"
58 "Default preload-target for registering autoloads"
59 :group 'elinstall
60 :type
61 '(choice
62 (const "~/.emacs.d/site-start.d/")
63 (const "/etc/emacs/site-start.d/")
64 (directory "" )
65 (const 'dot-emacs)))
68 (defcustom elinstall-already-installed
69 '()
70 "Things that have already been installed.
71 This exists for recording what has been installed. User interaction is not
72 contemplated at this time." )
74 ;;;_ , Data
75 ;;;_ . Regular expressions
76 ;;;_ , elinstall-elisp-regexp
77 (defconst elinstall-elisp-regexp
78 (let ((tmp nil))
79 (dolist
80 (suf (get-load-suffixes))
81 (unless (string-match "\\.elc" suf) (push suf tmp)))
82 (concat "^[^=.].*" (regexp-opt tmp t) "\\'"))
83 "Regular expression that matches elisp files" )
85 ;;;_ , Utilities
86 ;;;_ . elinstall-directory-true-name
87 (defun elinstall-directory-true-name ()
88 "Get the true name of the directory the calling code lives in.
89 CAUTION: This is sensitive to where it's called. That's the point of it."
90 (file-name-directory
91 (if load-file-name
92 (file-truename load-file-name)
93 (file-truename buffer-file-name))))
94 ;;;_ . Checking installedness
95 ;;;_ , elinstall-already-installed
96 (defun elinstall-already-installed (project-name)
97 "Return non-nil if PROJECT-NAME has been installed."
98 (member project-name elinstall-already-installed))
100 ;;;_ , elinstall-record-installed
101 (defun elinstall-record-installed (project-name)
102 "Record that PROJECT-NAME has been installed."
104 (add-to-list 'elinstall-already-installed project-name)
105 (customize-save-variable
106 'elinstall-already-installed
107 elinstall-already-installed
108 "Set by elinstall-record-installed"))
109 ;;;_ , Work
110 ;;;_ . Doing actions
112 ;;;_ , Doing autoload actions (All adapted from autoload.el)
113 ;;;_ . Utilities about the action list
114 ;;;_ , elinstall-remove-autogen-action
115 (defun elinstall-remove-autogen-action (file actions)
116 "Return ACTIONS minus any add-file-autoloads on FILE removed."
118 (delq nil
119 (mapcar
120 #'(lambda (act)
121 (case (car act)
122 (add-file-autoloads
123 (if (equal file (third act))
125 act))
126 (t act)))
127 actions)))
128 ;;;_ , elinstall-get-autogen-action
129 (defun elinstall-get-autogen-action (file actions)
131 (let
132 ((the-act))
133 (dolist (act actions)
134 (case (car act)
135 (add-file-autoloads
136 (when (equal file (third act))
137 (setq the-act act)))))
138 the-act))
140 ;;;_ . Making autoloads
141 ;;;_ , elinstall-generate-file-autoloads
142 ;;override to allow slashed load-paths
143 ;;Quick and dirty: We just adapt `generate-file-autoloads' and add
144 ;;a new arg.
145 ;;`relative-to' can be:
146 ;; * nil: act as at present. Assume that FILE's immediate directory
147 ;;is in load-path.
148 ;; * t :: use default-directory
149 ;; * a string :: relative to it, as a filename
151 (defun elinstall-generate-file-autoloads (relative-name full-name)
152 "Insert at point a loaddefs autoload section for FILE.
153 Autoloads are generated for defuns and defmacros in FILE
154 marked by `generate-autoload-cookie' (which see).
155 If FILE is being visited in a buffer, the contents of the buffer
156 are used.
157 Return non-nil in the case where no autoloads were added at point.
159 FULL-NAME is the absolute name of the file.
160 RELATIVE-NAME is its name respective to some component of load-path."
161 (let ((outbuf (current-buffer))
162 (autoloads-done '())
163 (print-length nil)
164 (print-readably t) ; This does something in Lucid Emacs.
165 (float-output-format nil)
166 (done-any nil)
167 (visited (get-file-buffer full-name))
168 (source-buf
169 (or visited
170 ;; It is faster to avoid visiting the file.
171 (autoload-find-file full-name)))
172 output-start)
174 (with-current-buffer source-buf
175 ;;$$MOVE ME - this should be checked in action-finding.
176 ;; Obey the no-update-autoloads file local variable.
177 (unless no-update-autoloads
178 (message "Generating autoloads for %s..." relative-name)
179 (setq output-start (with-current-buffer outbuf (point)))
180 (save-excursion
181 (save-restriction
182 (widen)
183 (goto-char (point-min))
184 (while (not (eobp))
185 (skip-chars-forward " \t\n\f")
186 (cond
187 ((looking-at (regexp-quote generate-autoload-cookie))
188 (search-forward generate-autoload-cookie)
189 (skip-chars-forward " \t")
190 (setq done-any t)
191 (if (eolp)
192 ;; Read the next form and make an autoload.
193 (let* ((form (prog1 (read (current-buffer))
194 (or (bolp) (forward-line 1))))
195 (autoload (make-autoload form load-name)))
196 (if autoload
197 (push (nth 1 form) autoloads-done)
198 (setq autoload form))
199 (let ((autoload-print-form-outbuf outbuf))
200 (autoload-print-form autoload)))
202 ;; Copy the rest of the line to the output.
203 (princ (buffer-substring
204 (progn
205 ;; Back up over whitespace, to preserve it.
206 (skip-chars-backward " \f\t")
207 (if (= (char-after (1+ (point))) ? )
208 ;; Eat one space.
209 (forward-char 1))
210 (point))
211 (progn (forward-line 1) (point)))
212 outbuf)))
213 ((looking-at ";")
214 ;; Don't read the comment.
215 (forward-line 1))
217 (forward-sexp 1)
218 (forward-line 1))))))
220 (when done-any
221 (with-current-buffer outbuf
222 (save-excursion
223 ;; Insert the section-header line which lists the file name
224 ;; and which functions are in it, etc.
225 (goto-char output-start)
226 (autoload-insert-section-header
227 outbuf autoloads-done relative-name full-name
228 (nth 5 (file-attributes full-name)))
229 (insert ";;; Generated autoloads from "
230 (autoload-trim-file-name full-name) "\n"))
231 (insert generate-autoload-section-trailer)))
232 (message "Generating autoloads for %s...done" relative-name))
234 (unless visited
235 ;; We created this buffer, so we should kill it.
236 (kill-buffer (current-buffer))))
237 (not done-any)))
240 ;;;_ , elinstall-deffile-insert-autoloads
241 (defun elinstall-deffile-insert-autoloads (file load-name)
242 "Update the autoloads for FILE in current buffer.
243 Return FILE if there was no autoload cookie in it, else nil.
245 Current buffer must be a loaddef-style file.
247 LOAD-NAME is the absolute name of the file.
248 RELATIVE-NAME is its name respective to some component of load-path."
249 (let (
250 (found nil)
251 (no-autoloads nil))
253 (save-excursion
254 (save-restriction
255 (widen)
256 (goto-char (point-min))
257 ;; Look for the section for FILE
258 (while (and (not found)
259 (search-forward generate-autoload-section-header nil t))
260 (let ((form (autoload-read-section-header)))
261 (cond
262 ((equal (nth 2 form) file)
263 ;; We found the section for this file.
264 (let ((begin (match-beginning 0)))
265 (progn
266 (search-forward generate-autoload-section-trailer)
267 (delete-region begin (point))
268 (setq found t))))
269 ((string< file (nth 2 form))
270 ;; We've come to a section alphabetically later than
271 ;; FILE. We assume the file is in order and so
272 ;; there must be no section for FILE. We will
273 ;; insert one before the section here.
274 (goto-char (match-beginning 0))
275 (setq found 'new)))))
276 (unless found
277 (progn
278 (setq found 'new)
279 ;; No later sections in the file. Put before the last page.
280 (goto-char (point-max))
281 (search-backward "\f" nil t)))
282 (setq no-autoloads
283 (elinstall-generate-file-autoloads file load-name))))
285 (if no-autoloads file nil)))
286 ;;;_ . Arranging to add to info-path and load-path
287 ;;;_ , elinstall-generate-add-to-path
288 (defun elinstall-generate-add-to-path (path-element type)
289 "Insert code at point to add PATH-ELEMENT to a path.
290 If TYPE is:
291 * `add-to-load-path', add to load-path
292 * `add-to-info-path', add to Info-default-directory-list
294 Current buffer must be a loaddef-style file."
295 (let ( (path-symbol
296 (case type
297 (add-to-load-path 'load-path)
298 (add-to-info-path 'Info-default-directory-list)))
299 (description
300 (case type
301 (add-to-load-path "load-path")
302 (add-to-info-path "info-path")))
303 (autoloads-done '())
304 (print-length nil)
305 (print-readably t) ; This does something in Lucid Emacs.
306 (float-output-format nil))
308 (message "Generating %s additions..." description)
310 (autoload-insert-section-header
311 (current-buffer) (list path-element) nil nil
312 nil)
313 (insert ";;; Generated path addition\n")
315 `(add-to-list ',path-symbol
316 (expand-file-name
317 ,(file-relative-name path-element)
318 (if load-file-name
319 (file-name-directory
320 (file-truename load-file-name)))))
321 (current-buffer))
323 (insert generate-autoload-section-trailer)
324 (message "Generating %s additions...done" description)))
327 ;;;_ , elinstall-deffile-insert-add-to-path
328 (defun elinstall-deffile-insert-add-to-path (path-element type)
329 "Insert code in current buffer to add PATH-ELEMENT to a path.
330 If TYPE is:
331 * `add-to-load-path', add to load-path
332 * `add-to-info-path', add to Info-default-directory-list
334 Current buffer must be a loaddef-style file."
335 (let (
336 (found nil)
337 (no-autoloads nil))
339 (save-excursion
340 (save-restriction
341 (widen)
342 (goto-char (point-min))
343 ;; Look for the section for PATH-ELEMENT
344 (while (and (not found)
345 (search-forward generate-autoload-section-header nil t))
346 (let ((form (autoload-read-section-header)))
347 (cond
348 ((and
349 (equal (nth 0 form) type)
350 (member (nth 1 form) path-element))
352 ;; We found the section for this add.
353 (let ((begin (match-beginning 0)))
354 (progn
355 (search-forward generate-autoload-section-trailer)
356 (delete-region begin (point))
357 (setq found t)))))))
359 (unless found
360 (progn
361 (setq found 'new)
362 ;; No later sections in the file. Put before the last page.
363 (goto-char (point-max))
364 (search-backward "\f" nil t)))
366 (elinstall-generate-add-to-path path-element type)))
368 ;;This never belongs in the no-autoloads section.
369 nil))
371 ;;;_ . elinstall-deffile-insert
373 (defun elinstall-deffile-insert (action)
374 "Insert autoloads etc into current file according to ACTION.
375 The format of ACTION is described in the design docs.
377 Return filename if this action belongs in the no-autoload section."
379 (when action
380 (case (car action)
381 (add-file-autoloads
382 (elinstall-deffile-insert-autoloads
383 (third action)
384 (fifth action)))
386 (add-to-load-path
387 (elinstall-deffile-insert-add-to-path
388 (third action)
389 'add-to-load-path)
390 nil)
392 (add-to-info-path
393 (elinstall-deffile-insert-add-to-path
394 (third action)
395 'add-to-info-path)
396 nil)
398 (preload-file
399 (error "This case should not come here.")))))
401 ;;;_ . elinstall-prepare-deffile
402 (defun elinstall-prepare-deffile (deffile)
403 "Try to ensure that DEFFILE is available for receiving autoloads"
405 (autoload-ensure-default-file deffile)
406 (with-current-buffer (find-file-noselect deffile)
409 ;; We must read/write the file without any code conversion,
410 ;; but still decode EOLs.
411 (let ((coding-system-for-read 'raw-text))
413 ;; This is to make generated-autoload-file have Unix EOLs, so
414 ;; that it is portable to all platforms.
415 (setq buffer-file-coding-system 'raw-text-unix))
416 (or (> (buffer-size) 0)
417 (error "Autoloads file %s does not exist" buffer-file-name))
418 (or (file-writable-p buffer-file-name)
419 (error "Autoloads file %s is not writable"
420 buffer-file-name))))
422 ;;;_ . elinstall-update-deffile
424 ;;Adapted from autoload.el `update-directory-autoloads'.
425 ;;Still being adapted:
427 ;; * Still need to treat add-to-info-path and
428 ;;add-to-load-path. Both recognize them and insert them.
429 ;; * Adapt `elinstall-update-file-autoloads' to understand actions.
431 ;; * Finding "file" among actions is rickety. Maybe knowing the
432 ;; respective load-path element would help.
434 (defun elinstall-update-deffile (target actions &optional
435 use-load-path force)
437 Update file TARGET with current autoloads as specified by ACTIONS.
438 Also remove any old definitions pointing to libraries that can no
439 longer be found.
441 ACTIONS must be a list of actions (See the format doc). Each one's
442 filename must be relative to some element of load-path.
444 USE-LOAD-PATH is a list to use as load-path. It should include
445 any new load-path that we are arranging to create. If it's not given,
446 load-path itself is used.
448 If FORCE is `t', do it regardless of timestamps etc. (Not implemented)
449 Other non-nil cases of FORCE are reserved for future development.
451 This uses `update-file-autoloads' (which see) to do its work.
452 In an interactive call, you must give one argument, the name
453 of a single directory."
454 (let
456 (use-load-path (or use-load-path load-path))
457 (this-time (current-time))
458 ;;files with no autoload cookies.
459 (no-autoloads nil))
461 (elinstall-prepare-deffile target)
462 (with-current-buffer
463 (find-file-noselect target)
464 (save-excursion
465 (setq actions
466 (elinstall-remove-autogen-action
467 (autoload-trim-file-name target)
468 actions))
470 (goto-char (point-min))
471 (while (search-forward generate-autoload-section-header nil t)
472 (let* ((form (autoload-read-section-header))
473 (file (nth 3 form)))
474 (cond ((and (consp file) (stringp (car file)))
475 ;; This is a list of files that have no
476 ;; autoload cookies.
477 ;; There shouldn't be more than one such entry.
478 ;; Remove the obsolete section.
479 (autoload-remove-section (match-beginning 0))
480 (let ((last-time (nth 4 form)))
481 (dolist (file file)
482 (let ((file-time (nth 5 (file-attributes file))))
483 (when (and file-time
484 (not (time-less-p last-time file-time)))
485 ;; file unchanged
486 (push file no-autoloads)
487 (setq actions
488 (elinstall-remove-autogen-action
489 file actions)))))))
490 ((not (stringp file)))
492 (let
493 ((file-path
494 (locate-library file nil use-load-path)))
495 (cond
496 ;;File doesn't exist, so remove its
497 ;;section.
498 ((not file-path)
499 (autoload-remove-section
500 (match-beginning 0)))
502 ;; File hasn't changed, so do nothing.
503 ((equal
504 (nth 4 form)
505 (nth 5 (file-attributes file-path)))
506 nil)
508 (elinstall-deffile-insert
509 (elinstall-get-autogen-action
510 file actions))))
512 (setq actions
513 (elinstall-remove-autogen-action
514 file actions))))))))
516 ;; Remaining actions have no existing autoload sections yet.
517 (setq no-autoloads
518 (append no-autoloads
519 (delq nil (mapcar #'elinstall-deffile-insert actions))))
520 (when no-autoloads
521 ;; Sort them for better readability.
522 (setq no-autoloads (sort no-autoloads 'string<))
523 ;; Add the `no-autoloads' section.
524 (goto-char (point-max))
525 (search-backward "\f" nil t)
526 (autoload-insert-section-header
527 (current-buffer) nil nil no-autoloads this-time)
528 (insert generate-autoload-section-trailer))
529 (save-buffer))))
532 ;;;_ , Doing actions to arrange preloads
533 ;;;_ . elinstall-insert-add-to-path
534 (defun elinstall-insert-add-to-path (new path-sym)
535 "Insert code to add NEW to PATH-SYM.
536 Insert this at point in current buffer."
537 (insert "\n")
539 `(add-to-list ',path-sym
540 (expand-file-name ,new
541 (if load-file-name
542 (file-name-directory
543 (file-truename load-file-name)))))
544 (current-buffer)))
546 ;;;_ . elinstall-insert-add-to-load-path
547 (defun elinstall-insert-add-to-load-path (new)
548 "Insert code to add NEW to load-path.
549 Insert this at point in current buffer."
550 (elinstall-insert-add-to-path new 'load-path))
552 ;;;_ . elinstall-insert-add-to-info-path
553 (defun elinstall-insert-add-to-info-path (new)
554 "Insert code to add NEW to info-path.
555 Insert this at point in current buffer."
556 (elinstall-insert-add-to-path new 'Info-default-directory-list))
558 ;;;_ . elinstall-symlink-on-emacs-start
559 (defun elinstall-symlink-on-emacs-start
560 (filename target-basename target-dir &optional priority force)
561 "Symlink to TARGET-BASENAME.el in TARGET-DIR
563 If PRIORITY is given, it will be used as the priority prefix,
564 otherwise elinstall-default-priority will be.
565 PRIORITY must be an integer or nil.
566 If FORCE is `t', do it regardless of timestamps etc.
567 Other non-nil cases of FORCE are reserved for future development."
568 (let*
570 (priority (or priority elinstall-default-priority))
571 (target-name-nodir
572 (format
573 "%d%s.el"
574 priority
575 target-basename))
576 (target
577 (expand-file-name target-name-nodir target-dir)))
580 (cond
581 ;;Path should already exist.
582 ((not
583 (file-exists-p target-dir))
584 (message "The target directory doesn't exist."))
585 ;;Target shouldn't already exist, but if force is given, let
586 ;;user override.
587 ((and
588 (file-exists-p target)
590 (not force)
591 (not
592 (yes-or-no-p
593 (format "Really overwrite %s? " project-name))))
594 (message "No symlink made to %s" target)))
597 (make-symbolic-link
598 filename
599 target
600 nil)))))
602 ;;;_ . elinstall-add-to-dot-emacs
603 (defun elinstall-add-to-dot-emacs (dot-emacs-name filename force &rest r)
604 "Add code to load FILENAME to .emacs.
605 FILENAME should not have an extension"
607 ;;Visit .emacs
608 (with-current-buffer (find-file-noselect dot-emacs-name)
609 (save-excursion
610 ;;add at end of file
611 (goto-char (point-max))
612 (insert "\n;;Added by elinstall")
613 (insert "\n;;Consider using my-site-start to manage .emacs\n")
614 (pp `(load ,filename) (current-buffer))
615 (save-buffer))))
618 ;;;_ . elinstall-link-on-emacs-start
619 ;;;###autoload
620 (defun elinstall-link-on-emacs-start (filename force basename priority)
623 ;;Figure out parameters, using defaults when needed.
624 (let*
625 ( (preload-target elinstall-default-preload-target))
627 ;;Dispatch the possibilities.
628 (cond
629 ((eq preload-target 'dot-emacs)
630 (elinstall-add-to-dot-emacs "~/.emacs" filename))
631 ((stringp preload-target)
632 (elinstall-symlink-on-emacs-start
633 filename basename preload-target priority force))
636 (message "I don't recognize that")))))
638 ;;;_ , Cleanup actions
639 ;;Nothing yet. This will be another type of action.
641 ;;;_ . Segregating actions
642 ;;;_ , elinstall-segregate-actions
643 (defun elinstall-segregate-actions (actions)
644 "Return actions segregated by deffile.
646 Returns a list whose elements are each a cons of:
647 * deffile filename or nil
648 * A list of actions to be done for that deffile."
650 (let
651 ((segment-list '()))
652 (dolist (act actions)
653 (when act
654 (let*
655 ( (deffile-name
656 (case (car act)
657 ((add-file-autoloads
658 add-to-info-path
659 add-to-load-path)
660 (second act))
661 (preload-file nil)))
663 (cell (assoc deffile-name segment-list)))
664 (if cell
665 (setcdr cell (cons act (cdr cell)))
666 (setq segment-list
667 (cons
668 (cons
669 deffile-name
670 (list act))
671 segment-list))))))
672 segment-list))
676 ;;;_ . Finding actions
677 ;;;_ , Treating the parameter list
678 ;;;_ . elinstall-add-parameter
679 (defun elinstall-add-parameter (alist key new-value)
680 "Add a new value for KEY to ALIST"
682 (cons
683 (cons key new-value)
684 (assq-delete-all key (copy-list alist))))
686 ;;;_ . elinstall-get-parameter
687 (defun elinstall-get-parameter (alist key)
688 "Get the value of KEY from ALIST"
690 (cdr (assq key alist)))
691 ;;;_ . elinstall-expand-file-name
692 ;;$$OBSOLETE
694 (defun elinstall-expand-file-name (filename alist)
695 "Expand FILENAME by the value of `path' in ALIST"
696 (expand-file-name
697 filename
698 (elinstall-get-parameter alist 'path)))
699 ;;;_ , Finding deffiles
700 ;;;_ . elinstall-expand-deffile-name
701 (defun elinstall-expand-deffile-name (deffile)
702 "Expand DEFFILE autoload.el's way."
704 (expand-file-name (or deffile "loaddefs.el")
705 (expand-file-name "lisp"
706 source-directory)))
708 ;;;_ . elinstall-maybe-get-deffile
709 (defun elinstall-maybe-get-deffile (file)
710 "If FILE defined `generated-autoload-file', return it.
711 Otherwise return nil.
712 Return it as an absolute filename."
714 (save-excursion
715 ;;$$FIXME load buffer if it's not already loaded
716 (let*
717 ((existing-buffer (get-file-buffer file)))
719 ;; We want to get a value for generated-autoload-file from
720 ;; the local variables section if it's there.
721 ;;But if it's not loaded, we don't? Maybe should use
722 ;; `autoload-find-file' and load it.
723 (if existing-buffer
724 (set-buffer existing-buffer))
725 (if (local-variable-p 'generated-autoload-file)
726 (elinstall-expand-deffile-name
727 generated-autoload-file)
728 nil))))
732 ;;;_ , elinstall-find-actions-by-spec
734 (defun elinstall-find-actions-by-spec (spec load-path-element path parameters)
735 "Return a list of actions to do, controlled by SPEC and PARAMETERS.
737 LOAD-PATH-ELEMENT is the conceptual element of load-path that
738 surrounds PATH. It may not yet have been added to load-path."
739 (if (consp spec)
740 ;;$$IMPROVE ME by adding the other cases in the design.
741 (case (car spec)
743 (let
744 ((new-path
745 (expand-file-name
746 (second spec)
747 path)))
749 (elinstall-find-actions-by-spec
750 (third spec)
751 load-path-element
752 new-path
753 parameters)))
755 (all
756 (apply #'append
757 (mapcar
758 #'(lambda (sub-spec)
759 (elinstall-find-actions-by-spec
760 sub-spec
761 load-path-element
762 path
763 parameters))
764 (cdr spec))))
765 ;;$$WRITEME
766 (file
769 (dir
770 (let
771 ((dirname
772 (expand-file-name
773 (second spec)
774 path))
775 (load-path-here
776 (not
777 (elinstall-get-parameter
778 parameters 'block-add-to-load-path))))
779 (cons
780 ;;$$IMPROVE ME
781 ;;Do this only if there are loadable files.
782 (if load-path-here
783 `(add-to-load-path
784 ,(elinstall-get-parameter
785 parameters 'def-file)
786 ,dirname)
787 '())
788 ;;$$IMPROVE ME
789 ;;Do add-to-info-path too. But test if there are
790 ;;any info files present.
792 ;;$$IMPROVE ME
793 ;; We want to get a value for generated-autoload-file
794 ;; from the local variables section if it's there.
795 ;;Use `elinstall-maybe-get-deffile'
796 ;; Otherwise we'll use `def-file' in parameters.
798 ;;$$FIXME This isn't quite right. If directory
799 ;;itself is not in load-path, this will be wrong.
800 ;;Gotta know where our encompassing part of
801 ;;load-path is.
803 ;;$$ENCAP ME This should be shared with the
804 ;;treatment of (file FN)
806 ;;$$FIXME Don't do directories, but maybe recurse on
807 ;;them, if a flag is set. And since we definitely
808 ;;have a load-path element here,
809 ;;'block-add-to-load-path according to a parameter.
810 ;;Maybe could follow/not symlinks similarly.
811 (mapcar
812 #'(lambda (filename)
813 (let
814 ((full-path
815 (expand-file-name filename dirname)))
816 `(add-file-autoloads
817 ,(elinstall-get-parameter
818 parameters 'def-file)
819 ,(file-name-sans-extension
820 (file-relative-name
821 full-path
822 load-path-element))
823 ,load-path-element ;;Is this still used?
824 ,full-path)))
826 (directory-files
827 dirname
828 nil ;;Relative filenames
829 elinstall-elisp-regexp)))))
831 (def-file
832 (let
833 ((new-def-file
834 (expand-file-name
835 (second spec)
836 path)))
837 (elinstall-find-actions-by-spec
838 (third spec)
839 load-path-element
840 path
841 (elinstall-add-parameter parameters
842 'def-file new-def-file)))))
844 ;;$$IMPROVE ME by adding the other cases in the design.
845 (case spec
846 (t))))
847 ;;;_ . high-level work
848 ;;;_ , elinstall-get-relevant-load-path
849 (defun elinstall-get-relevant-load-path (actions)
851 (delq nil
852 (mapcar
853 #'(lambda (act)
854 (case (car act)
855 (add-to-load-path
856 (second act))
857 (t nil)))
858 actions)))
860 ;;;_ , elinstall-do-segment
861 (defun elinstall-do-segment (segment force use-load-path)
862 "Do all the actions in SEGMENT.
863 FORCE has its usual meaning.
864 USE-LOAD-PATH is the effective load-path."
866 ;;$$IMPROVE ME - this will have to know the additions to load-path.
867 (let*
868 ((deffile (car segment)))
869 (if (stringp deffile)
870 (elinstall-update-deffile deffile (cdr segment) force
871 use-load-path)
872 ;;Do other actions: link-in actions and cleanups.
873 (mapcar
874 #'(lambda (act)
876 (case (car act)
877 (preload-file
878 (apply
879 #'elinstall-link-on-emacs-start
880 force
881 (cdr act)))
882 ;;$$PUNT do nothing.
883 (close-buffer)
884 (byte-compile)))
886 (cdr segment)))))
889 ;;;_ . Overall work
890 ;;;_ , elinstall-x
891 (defun elinstall-x (dir spec &optional preload-target force)
893 (let*
894 ((actions
895 (elinstall-find-actions-by-spec
896 spec
900 ;;$$RETHINK ME - maybe hand this work off to autoload?
901 ;;This is just the default loaddefs file, spec actually
902 ;;controls it.
903 (def-file . ,(elinstall-expand-deffile-name nil) ))))
904 (segment-list (elinstall-segregate-actions actions))
905 (use-load-path
906 (elinstall-get-relevant-load-path
907 actions)))
909 (mapcar
910 #'(lambda (segment)
911 (elinstall-do-segment segment force use-load-path))
912 segment-list)))
914 ;;;_ . Entry points
915 ;;;_ , elinstall
916 (defun elinstall (project-name dir spec &optional preload-target force)
917 "Install elisp files.
919 They need not be presented as a package.
921 Parameters:
922 PROJECT-NAME - the name of the project
923 DIR - the root directory of the project.
924 Suggestion: (elinstall-directory-true-name)
926 SPEC - a spec for the autoloads etc to make. It can be as simple as
927 `(in DIRECTORY t).
928 Suggestion: `(in ,(elinstall-directory-true-name) t)
929 PRELOAD-TARGET is where the autoload files are to be symlinked in. If
930 `nil' `elinstall-default-preload-target' is used instead.
932 If FORCE is t, install a package even if it has already been
933 installed.
934 Other non-nil cases of FORCE are reserved for future development."
936 (when
937 (and
938 (or
939 force
940 (not (elinstall-already-installed project-name)))
941 ;;$$RE-ADD ME - I commented it out just for development.
942 '(yes-or-no-p (format "Install %s " project-name)))
943 (elinstall-x dir spec preload-target force)
945 ;;$$RE-ADD ME - I commented it out just for development.
946 '(elinstall-record-installed project-name)))
950 ;;;_ , elinstall-update-directory-autoloads
951 ;;$$TEST ME
952 (defun elinstall-update-directory-autoloads (dir)
955 (interactive "DInstall all elisp files from directory: ")
957 (elinstall-x
959 '(dir ".")
960 (elinstall-expand-deffile-name
961 generated-autoload-file)))
964 ;;;_ , elinstall-update-file-autoloads
965 ;;$$TEST ME
966 (defun elinstall-update-file-autoloads (file)
969 (interactive "fInstall elisp file: ")
970 (elinstall
971 file
972 `(file ,file)
974 (elinstall-maybe-get-deffile file)
975 (elinstall-expand-deffile-name
976 generated-autoload-file))))
983 ;;;_. Footers
984 ;;;_ , Provides
986 (provide 'elinstall)
988 ;;;_ * Local emacs vars.
989 ;;;_ + Local variables:
990 ;;;_ + mode: allout
991 ;;;_ + End:
993 ;;;_ , End
994 ;;; elinstall.el ends here