From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: Stefan Newsgroups: gmane.emacs.devel Subject: Re: `make' written in elisp Date: Sun, 02 Jan 2005 19:26:41 -0500 Message-ID: References: NNTP-Posting-Host: deer.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: sea.gmane.org 1104712032 16605 80.91.229.6 (3 Jan 2005 00:27:12 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Mon, 3 Jan 2005 00:27:12 +0000 (UTC) Cc: emacs-devel@gnu.org, rms@gnu.org, schierlm@gmx.de Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Mon Jan 03 01:27:05 2005 Return-path: Original-Received: from lists.gnu.org ([199.232.76.165]) by deer.gmane.org with esmtp (Exim 3.35 #1 (Debian)) id 1ClG3q-0000y0-00 for ; Mon, 03 Jan 2005 01:27:02 +0100 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1ClGEv-0008JD-Sk for ged-emacs-devel@m.gmane.org; Sun, 02 Jan 2005 19:38:29 -0500 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.33) id 1ClGEm-0008J8-0U for emacs-devel@gnu.org; Sun, 02 Jan 2005 19:38:20 -0500 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.33) id 1ClGEl-0008Ir-FL for emacs-devel@gnu.org; Sun, 02 Jan 2005 19:38:19 -0500 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1ClGEl-0008Io-C9 for emacs-devel@gnu.org; Sun, 02 Jan 2005 19:38:19 -0500 Original-Received: from [206.47.199.164] (helo=simmts6-srv.bellnexxia.net) by monty-python.gnu.org with esmtp (Exim 4.34) id 1ClG3X-0008Aa-Iq; Sun, 02 Jan 2005 19:26:43 -0500 Original-Received: from empanada.home ([67.71.119.166]) by simmts6-srv.bellnexxia.net (InterMail vM.5.01.06.10 201-253-122-130-110-20040306) with ESMTP id <20050103002642.VMNH1597.simmts6-srv.bellnexxia.net@empanada.home>; Sun, 2 Jan 2005 19:26:42 -0500 Original-Received: by empanada.home (Postfix, from userid 502) id 2D0EE3CD457; Sun, 2 Jan 2005 19:26:42 -0500 (EST) Original-To: David Kastrup In-Reply-To: (David Kastrup's message of "Mon, 03 Jan 2005 00:22:31 +0100") User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/21.3.50 (darwin) 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: main.gmane.org gmane.emacs.devel:31709 X-Report-Spam: http://spam.gmane.org/gmane.emacs.devel:31709 --=-=-= > Judging from the comments, this installer might more or less get along > at least with some XEmacs packages, as well as some other packages > with a regular enough structure (AUCTeX has in the mean time gotten a > new autoconf-based installer very similar to that of preview-latex, so > the latest versions probably will not work with install.el). It might still work, depending on how the configuration is done. I tend to think that most of the autoconfiguration should be done upon use rather than during installation, which basically implies "in elisp rather than in autoconf". > a) a directory layout. XEmacs packages have a standard way of placing > Elisp, info, data and other files in a layout under the top package > directory. In addition there are some standard file names for > initializing of packages and installation. In short, this part of the > package system is basically just file layout conventions relative to a > package-specific top directory. > We don't have any conventions for packages intended for Emacs as far > as I can see. It certainly would not be bad to have something like > that: if people make code for Emacs, and if we can agree on some > layout that makes it easier for stuff to work out of the box, it > certainly might be worthwhile to state some conventions. Yes, `install' does not impose a particular structure (it just looks recursively for *.el files). But it would benefit from some conventions (so it can safely ignore the contrib/foo.el or compat/bar.el files). I can't find any documentation about the in-package layout used by XEmacs, tho. Anybody knows where it's described? > b) version management. Versions must basically be floats, higher > versions correspond to later versions, are usually based on some CVS > archive and not at all with actual package versions. Uh. Right, it seems to be used only to discover whether a newer version is available or not. > c) a package manager that will download and upgrade packages. It > might do so in user-specific and system-wide packages. > d) central download archives that will provide all packages, updated > by packagers (often different from the actual creators of software) on > central servers. > e) it does not offer AFAIR: dependencies and standard tests (for > executables, directories and other stuff) that autoconf provides. A > bit of framework for that might be nice for obliterating some > necessities for installation. Even though installing MSYS is not > particularly hard, it still seems to be a barrier for some people. `install' doesn't get involved with version management, dependencies, ... It only tries to make it easier for users to install and setup external packages. A package manager to also allow activation/deactivation/deletion would be a natural addition. > I am not convinced about the value of the centralized server approach > to package management from XEmacs: it does not seem to lend itself Since `install' specifically targets external packages, a centralized server doesn't make much sense indeed. For XEmacs, it was probably a good simplification. > But I do think that we should try to offer some way of installing > external Lisp code if one has acquired it as a zip or tar archive in > some manner already. Not just tarballs but also single elisp files. Most elisp packages are single files. > And in those respects where the directory structure and layout > conventions of an XEmacs package would seem reasonable, there seems > little point in inventing something else. Agreed. > Stefan, having met those problems when writing install.el already, > would probably be a lot more qualified to comment on my impressions > and on the versatility of the package layout he has been catering for. The DWIM-aspect of `install' (just look for *.el and *.info files) only works up to a point. I've bumped into problems where some *.el files should be ignored, or other problems where the *.info file doesn't have the `info' extension, or where the info file simply doesn't exist and it's not clear how to build it based on a bunch of *.texi files, ... We could keep adding intelligence and heuristics, but it makes more sense to devise some conventions. > All of those certainly sound reasonable. In particular, I'd second having > separate commands for installing system-wide and user-local packages. Agreed, it makes more sense. Stefan PS: I attached my latest version, which is still "work in progress". --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=install.el Content-Transfer-Encoding: quoted-printable ;;; install.el --- Package to ease installation of Elisp packages ;; Copyright (C) 2001, 2003, 2004 Stefan Monnier ;; Author: Stefan Monnier ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; This little package is meant to ease up the task of installing ;; third party ELisp packages. I.e. it takes care of placing it in ;; an appropriate location, finds out what code is necessary to get ;; the package activated, sets up your .emacs file to activate the ;; package, and byte-compiles the package. ;; It should work on both single-file packages and tarballs. ;; On tarball packages, it does a bit of guess work to figure out ;; where are which files and how to use them. This is bound to ;; fail sometimes. ;; Tested on: ;; + ProofGeneral ;; + sml-mode ;; + AUCTeX (missed the info page) ;; + X-Symbol (as an XEmacs package) ;; + Gnus (but doesn't install the info doc :-( ) ;; + BBDB (misses the main info page) ;; - WhizzyTeX (needs to hack the perl script and stuff :-( ) ;; ? ECB ;; ? JDEE ;; ? preview-latex ;; ? VM ;; ? mmm-mode ;; ? Semantic ;; The on-disk structure is as follows: ;; - there are two area: the `home' and the `site' each with their ;; respective directory (~/lib/emacs and /usr/share/emacs/site-lisp) ;; and file (.emacs and site-start). ;; - There is a distinction between installing and activating. ;; Installing only places the files on disk, whereas activating sets up ;; autoloads and friends. ;; - Activation is done on a directory by directory basis. Each directory ;; has an `autoloads' file. Loading it activates the package(s) ;; in directory. ;; - Single-file packages are placed together in the toplevel directory ;; whereas tarball-packages are placed in their own subdirectory (so they ;; can be activated independently). ;;; Todo: ;; - don't ask whether to activate site-wide packages installed in home. ;; - Create Info from Texinfo when needed. ;; - Try harder to find Info files such as doc/auctex. ;; - UI to (un)install and (de)activate packages, get a list, ... ;; - If a single-file package lacks ;;;###autoload, try to add them ;; based on the Commentary section or something. ;;; Code: (require 'em-glob) (defgroup install nil "Elisp package installation") (defmacro install-filter (list exp) (declare (debug t)) `(let ((res nil)) (dolist (x ,list (nreverse res)) (if ,exp (push x res))))) (defcustom install-site-file (or (locate-file (or site-run-file "site-start") load-path load-suffixes) (let ((lp (mapcar 'abbreviate-file-name load-path))) ;; Prefer non-user directories. (setq lp (or (install-filter lp (not (string-match "\\`~/" x))) lp)) ;; Prefer site-lisp directories. (setq lp (or (install-filter lp (string-match "/site-lisp\\'" x)) lp)) ;; Prefer shorter directory names (i.e. parents rather than subdirs). (setq lp (sort lp (lambda (d1 d2) (< (length d1) (length d2))))) ;;=20 (expand-file-name (concat (or site-run-file "site-start") ".el") (car = lp)))) "Site-wide customization file." :type 'file) (defcustom install-site-dir (file-name-directory install-site-file) "Directory where site-wide packages should be installed." :type 'directory) (defcustom install-home-file (or user-init-file (convert-standard-filename "~/.emacs")) "Main customization file into which Install should place `load' commands." :type 'file) (defcustom install-home-dir ;; FIXME: We should be careful never to choose one of Emacs's own ;; directories, even if the user installed Emacs in his home dir. (let ((lp (mapcar 'abbreviate-file-name load-path))) ;; Only consider writable directories. (setq lp (install-filter lp (file-writable-p x))) ;; Only consider user directories. (setq lp (install-filter lp (string-match "\\`~/" x))) ;; Prefer shorter directory names (i.e. parents rather than subdirs). (setq lp (sort lp (lambda (d1 d2) (< (length d1) (length d2))))) ;; Default to ~/lib/emacs. (if (or (null lp) ;; If it's a subdir of lib/emacs, use lib/emacs. This can happen ;; because Install does not automatically add lib/emacs to the ;; load-path if it only installs tar packages underneath. (string-match "\\`~/lib/emacs/" (car lp))) "~/lib/emacs/" (car lp))) "Directory into which elisp packages should be placed by Install." :type 'directory) (defcustom install-autoload-file "autoloads" "Name of autoload files used by Install.") (defcustom install-compress-source-files nil ;; ".gz" "If non-nil, Install will try to compress file.") (defcustom install-byte-compile t "If non-nil, elisp files are byte-compiled during installation." :type 'boolean) ;; (defun install-get-dir () "Return the directory into which to install packages." (or (and (file-writable-p install-site-dir) (y-or-n-p "Install site-wide? ") install-site-dir) (progn (unless (file-writable-p install-home-dir) (setq install-home-dir (let ((default-directory install-home-dir)) (read-directory-name "Directory to install into: "))) (unless (file-directory-p install-home-dir) (make-directory install-home-dir t))) install-home-dir))) (defun install-get-file () "Return the file into which to activate packages." (or (and (file-writable-p install-site-file) (y-or-n-p "Activate site-wide? ") install-site-file) install-home-file)) (defmacro install-with-file (file &rest body) (declare (debug t) (indent 1)) `(let ((install-with-existing-file (find-buffer-visiting ,file))) (with-current-buffer (or install-with-existing-file (find-file-noselect ,file)) (prog1 (save-current-buffer ,@body) (unless install-with-existing-file (kill-buffer (current-buffer))))))) ;;;###autoload (defun install-file (file) (interactive "fFile to install: ") (with-current-buffer (find-file-noselect file) (install-buffer))) ;;;###autoload (defun install-buffer () "Install the current elisp buffer as a package. The package is install in `install-home-dir', autoloads are added to the `install-autoload-file' in that directory and the `install-custom-file' is then updated to load these autoloads." (interactive) (cond ((derived-mode-p 'tar-mode) (install-tar-buffer)) ((not (derived-mode-p 'emacs-lisp-mode)) (error "I only know how to install tar.gz and elisp files.")) (t (let* ((install-dir (install-get-dir)) (package (file-name-nondirectory buffer-file-name)) (file (expand-file-name package install-dir)) (autoload (expand-file-name install-autoload-file install-dir))) (when (and install-compress-source-files (string-match "\\.el\\'" file)) (setq file (concat file (if (stringp install-compress-source-files) install-compress-source-files ".gz")))) ;; Install the elisp file. (write-region (point-min) (point-max) file) ;; Extract the autoloads into a separate file. (install-update-autoloads autoload) ;; Activate. (install-activate autoload) ;; Finally, byte compile. In the present case (a single-file package= ), ;; this could be done before activation. (if install-byte-compile (byte-compile-file file)))))) (defun install-tar-buffer () "Like `install-buffer' but for a tar package rather than single file." (let* ((name (file-name-nondirectory buffer-file-name)) ;; Strip off ".tar.gz", ".tar", ".tgz", ".tar.Z", ... (name (if (string-match "\\.[tT][^.]+\\(\\.[^.]+\\)?\\'" name) (substring name 0 (match-beginning 0)) name)) (install-dir (install-get-dir)) (default-directory (expand-file-name name install-dir))) ;; Install the files. ;; FIXME: check what `tar-untar-buffer' does with symlinks and stuff. ;; FIXME: the dir might already exist. (make-directory default-directory) (tar-untar-buffer) (let ((files (directory-files default-directory nil "\\`\\([^.]\\|\\.[^.]\\|\\.\\..\\)" t))) ;; If the tar file already had everything under a single directory, ;; remove the redundant level of directory. (when (and (=3D (length files) 1) (file-directory-p (car files))) (let* ((f (car files)) ;; Keep the longest name of the two, assuming that the ;; difference is that the longer one has a version number. (final (if (> (length name) (length f)) name f)) (temp (if (=3D (length name) (length f)) (concat f ".tmp") f))) ;; FIXME: the dir might already exist. (rename-file f (expand-file-name temp install-dir)) (setq default-directory install-dir) (delete-directory name) ;; FIXME: the dir might already exist. (unless (equal final temp) (rename-file temp final)) (setq name final) (setq default-directory (expand-file-name final))))) (install-directory-inplace default-directory))) ;;;###autoload (defun install-directory-inplace (dir) "Prepare and activate the current directory for use by Emacs. Sets up the autoload files, activates them and byte-compiles if needed." (interactive (list (if (derived-mode-p 'dired-mode) list-buffers-directory (read-directory-name "Directory to install: " nil nil t)))) (let ((default-directory (expand-file-name dir))) ;; Extract the autoloads. (install-setup-tree) ;; Activate the package. (install-activate (expand-file-name (install-get-activation-file))) ;; Finally, byte-compile the files. (if install-byte-compile (install-byte-compile-dir)))) (defun install-dirs-of-files (files) "Return a list of subdirs containing elisp files." (let ((dirs nil) (ignore (regexp-opt (cons ;; Ignore contrib directories because they tend to contain ;; either less-debugged code, or packages that might ;; already be installed and can thus interfere. "contrib/" (let ((exts nil)) (dolist (ext completion-ignored-extensions exts) (if (eq (aref ext (1- (length ext))) ?/) (push ext exts)))))))) ;; Collect the dirs that hold elisp files. (dolist (file files dirs) (let ((dir (file-name-directory file))) (unless (or (member dir dirs) (and dir (string-match ignore dir))) (push dir dirs)))))) (defun install-find-elisp-dirs () "Return a list of subdirs containing elisp files." (install-dirs-of-files (install-glob "**/*.el"))) (defun install-byte-compile-dir () "Byte compile all elisp files under the current directory." (let ((load-path (append (mapcar (lambda (dir) (if dir (expand-file-name dir) default-directory)) (install-find-elisp-dirs)) load-path))) (byte-recompile-directory default-directory 0))) (defun install-glob (pattern) (let ((res (eshell-extended-glob pattern))) (if (listp res) res))) (defun install-get-activation-file () "Return the file to load to activate the package. This is usually \"./autoloads\", but it can also be \"lisp/foo-site.el\"." (if (file-exists-p install-autoload-file) install-autoload-file (or (car (install-glob (concat "**/" install-autoload-file))) (car (install-glob "**/auto-autoloads.el")) (car (install-glob "**/*-site.el"))))) (defun install-setup-tree () (eshell-glob-initialize) ;; Look for elisp files. (let ((dirs (install-find-elisp-dirs)) (autoload-files nil) (toplevel nil)) ;; Prepare each elisp subdir and collect info along the way. ;; PROBLEM: if a dir that contains elisp files contains no autoload, we ;; don't know whether to add it to load-path or not: if it's a library ;; it should be added, but it might be some unimportant `contrib' or ;; testing code that should be left alone. (dolist (dir dirs) (let ((default-directory (expand-file-name (or dir default-directory)= ))) ;; Remove *.elc files, in case they were not compiled for our version. (mapc 'delete-file (install-glob "*.elc")) ;; Extract autoloads. (let ((sites (or (install-glob "auto-autoloads.el") (install-glob "*-load.el") (install-glob "*-site.el")))) (if (=3D 1 (length sites)) ;; Some packages come with a -site.el file instead ;; of using autoloads. In that case, just load that file. (progn (install-sanitize-autoloads-file (car sites)) (push (concat dir (car sites)) autoload-files)) ;; Otherwise. Make an autoloads file and load it. ;; FIXME: Don't make hundreds of autoload files. (let ((exists (file-exists-p install-autoload-file))) (if (not (install-update-autoloads install-autoload-file)) ;; Don't stupidly add empty autoloads files. ;; PROBLEM: this removes DIR from load-path as well. (unless exists (delete-file install-autoload-file)) (push (concat dir install-autoload-file) autoload-files))))))) (unless autoload-files ;; We didn't find any autoload file. I.e. there's nothing to load. ;; Most likely this package should be used via `require', like elib. (setq autoload-files (mapcar (lambda (dir) (concat dir install-autoload-file)) dirs)) (mapc 'install-sanitize-autoloads-file autoload-files)) ;; Setup the toplevel activation file. (if (and (=3D 1 (length autoload-files)) (equal (car autoload-files) (install-get-activation-file))) (setq toplevel (car autoload-files)) (setq toplevel install-autoload-file) (dolist (file autoload-files) (unless (equal file toplevel) (install-activate `(expand-file-name ,(file-relative-name (expand-file-name file) (file-name-directory (expand-file-name toplevel))) (file-name-directory load-file-name)) toplevel)))) ;; Make up an info/dir file if necessary and register the info dirs. (let ((info-dirs (install-make-info))) (when info-dirs (install-with-file toplevel (unless (derived-mode-p 'emacs-lisp-mode) (emacs-lisp-mode)) (goto-char (point-min)) (unless (re-search-forward "(add-to-list[ \t\n]+'Info-default-directory-= list" nil t) (forward-comment (point-max)) (while (re-search-backward "^ " nil t)) (unless (bolp) (newline)) (let ((top-dir (file-name-directory (expand-file-name toplevel)))) (dolist (dir info-dirs) (setq dir (expand-file-name (or dir default-directory))) (if (equal dir top-dir) (insert "(add-to-list 'Info-default-directory-list (file-name-directo= ry load-file-name))\n") (let ((text (pp-to-string (file-relative-name dir top-dir)))) (if (string-match "\n\\'" text) (setq text (substring text 0 -1))) (insert "(add-to-list 'Info-default-directory-list\n" " (expand-file-name " text " (file-name-directory load-file-name)))\n"))))) (save-buffer 0))))))) (defun install-sanitize-autoloads-file (file) "Make sure that the autoload file FILE exists and behaves sanely." (install-with-file file (unless (derived-mode-p 'emacs-lisp-mode) (emacs-lisp-mode)) ;; Make file and buffer writable (for CVSREAD=3Don kind of situations). (when buffer-read-only (set-file-modes buffer-file-name (logior ?\200 (file-modes buffer-file-name))) (toggle-read-only)) (goto-char (point-min)) ;; Insert a little boiler plate if there's nothing yet. (when (eobp) (insert ";;; " (file-name-nondirectory file) " --- automatically extracted autoloads\n" ";;\n" ";;; Code:\n\n" " \n;; Local Variables:\n" ";; version-control: never\n" ";; no-byte-compile: t\n" ";; no-update-autoloads: t\n" ";; End:\n" ";;; " (file-name-nondirectory file) " ends here\n") (goto-char (point-min))) ;; Make sure it will setup the load path properly. (unless (re-search-forward "\\" nil t) (forward-comment (point-max)) (while (re-search-backward "^ " nil t)) (unless (bolp) (newline)) (unless (eq (char-before (1- (point))) ?\n) (newline)) (insert ";; Tell Emacs to look for elisp files in this directory." ;; Add some sort of signature. " -- Install\n") (insert "(add-to-list 'load-path (or (file-name-directory load-file-name) (car load-path)))\n\= n") (save-buffer 0))) file) (defvar generated-autoload-file) (defun install-update-autoloads (autoload) "Update file AUTOLOAD. This will create the file if necessary. Returns non-nil if there is anything autoloaded into it." (setq autoload (expand-file-name autoload)) (let ((bufp (find-buffer-visiting autoload))) (let ((generated-autoload-file (install-sanitize-autoloads-file autoloa= d))) ;; (update-file-autoloads file) (update-directory-autoloads (file-name-directory autoload))) ;; Make sure the file sets up the load-path appropriately. (with-current-buffer (find-file-noselect autoload) (unless (derived-mode-p 'emacs-lisp-mode) (emacs-lisp-mode)) (goto-char (point-min)) (re-search-forward "^ " nil t) ;Find the first autoload entry. (forward-comment (point-max)) (prog1 (not (eobp)) (unless bufp (kill-buffer (current-buffer))))))) (defun install-activate (autoload &optional into) "Update INTO to make sure it loads AUTOLOAD. AUTOLOAD can be an expression. If it is a string, this also loads it into the currently running Emacs. If provided, INTO specifies the file which should load AUTOLOAD. The default is to use `install-get-file'." (when (stringp autoload) (setq autoload (abbreviate-file-name autoload)) (load autoload)) (install-with-file (or into (install-get-file)) (unless (derived-mode-p 'emacs-lisp-mode) (emacs-lisp-mode)) (save-excursion (let ((text (pp-to-string autoload))) (if (string-match "\n\\'" text) (setq text (substring text 0 -1))) (goto-char (point-min)) (unless (re-search-forward (regexp-quote text) nil t) (goto-char (point-min)) (forward-comment (point-max)) (while (re-search-backward "^ " nil t)) (unless (bolp) (newline)) ;; Pass `install' as argument to load: this both makes Emacs ;; ignore the load if the file is missing and is used as a marker ;; indicating that this load statement was introduced by us. (insert "(load " text " 'install)\n") (save-buffer)))))) ;;;###autoload (defun install-list-packages () "Show the installed packages." (interactive) (dired (install-get-dir))) ;; Info files and DIR files. ;; Some of this should probably be moved to info.el. (defconst install-info-dir "-*- Text -*-\n=1F\n\ File: dir Node: Top This is the top of the INFO tree\ \n\n* Menu:\n\n" "Text content of a barebones empty `info/dir' file.") (defun install-find-info-files () (let ((files (or (install-glob "**/*.info*") (install-glob "**/info/*"))) (tmp nil)) (dolist (f files) (unless (or (member f '("dir" "localdir")) (and (string-match "-[0-9]+" f) (member (replace-match "" t t f) files)) (not (string-match "\\.info\\>\\|\\(\\`\\|/\\)[^.]+\\(\\'\\|\\.\\(gz\\|= Z\\)\\)" f))) (push f tmp))) tmp)) (defun install-make-info () "Make an info/dir file if necessary and return the info directories." ;; FIXME: This should create the info files from the Texinfo files ;; if necessary !! ;; Problems to do that: ;; - detect when necessary. E.g. BBDB comes with an info page for ;; the bbdb-filters stuff, but the main bbdb doc is in texinfo. ;; - figure out how to makeinfo the thing. E.g. AucTeX comes with ;; a whole bunch of Texinfo files and it's really not clear which ;; is the right one. ;; - The info file might be there, but not found. E.e. AucTeX has its ;; page in doc/auctex. (let* ((files (install-find-info-files)) (dirs (install-dirs-of-files files)) (dir-files nil)) ;; Remove files that were in ignored directories. (dolist (file files) (unless (member (file-name-directory file) dirs) (setq files (delq file files)))) ;; Check that there's something to do. (when files (assert dirs) (dolist (dir dirs) (if (file-exists-p (expand-file-name "dir" dir)) (push (expand-file-name "dir" dir) dir-files))) (unless dir-files ;; Pick the dir closest to the toplevel to put the main dir file. (setq dirs (sort dirs (lambda (s1 s2) (< (length s1) (length s2))))) (install-with-file (expand-file-name "dir" (car dirs)) (assert (=3D (point-min) (point-max))) (insert install-info-dir) (narrow-to-region (point) (point-max)) (dolist (file files) (let ((section "Miscellaneous") (entry nil)) (install-with-file file (goto-char (point-min)) (if (not (re-search-forward (concat "^START-INFO-DIR-ENTRY\n" "\\([* \t].*\n\\)+" "END-INFO-DIR-ENTRY$") nil t)) ;; No entry in the file, let's build a default one. (let ((base (file-name-nondirectory (file-name-sans-extension file)))) (setq entry (concat "* " (upcase base) ": (" base ").\n"))) (setq entry (match-string 1)) (goto-char (point-min)) (when (re-search-forward "^INFO-DIR-SECTION[ \t]+\\(.*[^ \t\n]\\)" nil t) (setq section (match-string 1))))) (goto-char (point-min)) (unless (search-forward entry nil t) (unless (re-search-forward (concat "^" (regexp-quote section) "[ \t]*\n")= nil 'move) (unless (bobp) (newline)) (insert section) (newline)) (insert entry)))) (save-buffer 0) (kill-buffer (current-buffer)))) dirs))) =09=20=20=20=20=20=20 =09=20=20=20=20=20=20 (provide 'install) ;; arch-tag: 7f3dd74d-b21f-462e-91bd-1e53fa19a2ee ;;; install.el ends here --=-=-= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://lists.gnu.org/mailman/listinfo/emacs-devel --=-=-=--