;;; 	build.el,v 1.22 2001/03/11 23:43:28 aichnerad Exp

;;{{{ Legalese

;; Copyright (C) 1997-2001 Adrian Aichner

;; Author: Adrian Aichner <adrian@xemacs.org>
;; Date: $Date: 2001/04/29 22:37:04 $
;; Version: $Revision: 1.39 $
;; Keywords: internal

;; This file is part of XEmacs.

;; XEmacs 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.

;; XEmacs 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 XEmacs; see the file COPYING.  If not, write to the Free
;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
;; 02111-1307, USA.

;;; Synched up with: Not synched.

;;}}}

;;{{{ require/provide

(require 'custom)
(require 'cus-edit)
(require 'widget)
(require 'build-report)

(autoload 'ring-insert-at-beginning "ring")
(autoload 'efs-copy-file "efs")

;; `url-copy-file' (buffer: build.el, mode: Lisp)

(eval-when-compile
  (require 'cl))

;; Pull in compile, if it is available.
(condition-case nil
    (require 'compile)
  (error nil))

(eval-when-compile
  (require 'wid-edit))

;; Pull in pcl-cvs, if it is available.
(condition-case nil
    (require 'pcl-cvs)
  (error nil))

(provide 'build)

;;}}}

;;{{{ Version info

;;;
;;; Version-handling, based on ideas from w3.
;;;
(defconst build-version-number
  (let ((x "1.01"))
    (if (string-match "Name:[ \t\n]+\\([^\n]+\\) \\$" x)
	(setq x (match-string 1 x))
      (setq x (substring x 0)))
    (mapconcat
     (function (lambda (x) (if (= x ?-) "." (char-to-string x)))) x ""))
  "Version # of build.")

(defconst build-version-date
  (let ((x "2001-04-30"))
    (if (string-match "Date:[ \t\n]+\\([^\n]+\\) \\$" x)
	(match-string 1 x)
      x))
  "Date this version of build was released.")

(defconst build-version
  (format "build %s %s" build-version-number build-version-date)
  "More descriptive version of build-version-number.")

;;;###autoload
(defun build-version (&optional here)
  "Show the version number of `build' in the minibuffer.
If optional argument HERE is non-nil, insert info at point."
  (interactive "P")
  (if here
      (insert build-version)
    (if (interactive-p)
        (message "%s" build-version)
      build-version)))
;;}}}

;;{{{ Build Compilation

;;{{{ Compilation
(make-variable-buffer-local
 'compilation-finish-function)
(make-variable-buffer-local
 'compilation-exit-message-function)

(setq
 compilation-finish-function
 'build-compilation-finish-function
 compilation-exit-message-function
 (function build-compilation-exit-message-function))

;;}}}

(defun build-compilation-mode-hook ()
  (set (make-local-variable 'auto-save-hook)
       '(lambda ()
          (message "Auto-saved %s\n" (buffer-name))))
  (auto-save-mode 1)
  (insert "Compilation started at " (current-time-string) "\n"))

(defun build-compilation-finish-function (comp-buffer finish-string)
  (message "Build Make finished in %s with status \"%s\"."
           (buffer-name comp-buffer) finish-string))

(defun build-compilation-exit-message-function (proc exit-msg)
  (message "Build Make exited with proc status \"%s\", exit status \"%s\", exit message \"%s\"."
           (process-status proc) (process-exit-status proc) exit-msg)
  (cons exit-msg (process-exit-status proc)))

;;}}}

;;{{{ Build Configure

(defconst build-configure-option-category
  "^\\(\\S-+\\).+\\(options\\|features\\):$"
  "REGEXP matching an XEmacs configuration option category in
configure.usage")

(defconst build-configure-option-paragraph
  "^\\(--[a-zA-Z][-a-zA-Z0-9]+\\)\\(=\\(\\S-+\\)\\)?\\(\\s-+(\\*)\\)?\\s-+\\(\\(.+\\)\\(\n[ \t]+.+\\)*\\)$"
  "REGEXP matching one XEmacs configuration option in
configure.usage")

(defun build-configure (&optional dir)
  "Configure XEmacs according to the settings in customized group
`build' and its members."
  (interactive)
  (if dir
      (cd dir))
  (let ((cmd
         (format "sh configure%s"
                 (mapconcat
                  (function (lambda (e)
                              (cond
                               ((or (string= "" (rest e))
                                    (string= "autodetected" (rest e))
                                    (string= "defaulted" (rest e)))
                                "")
                               ((string= "yes" (rest e))
                                (format " '%s'" (first e)))
                               ((and
                                 (string-match "\\`--without-\\(.+\\)\\'" (first e))
                                 (string= "no" (rest e)))
                                (format " '-with-%s'" (match-string 1 (first e))))
                               (t
                                (format " '%s=%s'" (first e) (rest e))))))
                  (delete-duplicates
                   build-configure-options :from-end t
                   :test (lambda (a b)
                           (string=
                            (first a) (first b))))
                  "")))
        (compilation-mode-hook
         'build-compilation-mode-hook)
        (compilation-buffer-name-function
         '(lambda (mode)
            (generate-new-buffer-name
             (cond
              ((string-equal build-from-what "Tarballs")
               (concat build-tarball-prefix "-configure.err"))
              ((string-equal build-from-what "CVS")
               (concat build-cvs-checkout-dir "-configure.err")))
             ))))
    (compile cmd)))

;;; Functionality which was prototyped in co2cu.el:

(defun build-configure-customize (a-list)
  (mapcar
   (lambda (cat)
     (princ (format "(defgroup build-configure-%s nil\n" (first cat)))
     (princ (format "  \"%s options.\"\n" (first cat)))
     (princ "  :group 'build-configure)\n\n")
     (list (first cat)
           (mapcar
            (lambda (opt)
              (cond
               ((or (member "TYPE[,TYPE]..." (second opt))
                    (and (member "TYPE" (second opt))
                         (string-match
                          "list\\s-+of"
                          (apply 'concat (fourth opt)))))
                (build-configure-types cat opt)
                )
               ((member "TYPE" (second opt))
                (build-configure-type cat opt)
                )
               ((member "FLAGS" (second opt))
                (build-configure-string cat opt)
                )
               ;; compiler=XXXX prior to r21.0-b34
               ((member "XXXX" (second opt))
                (build-configure-file cat opt)
                )
               ;; compiler=prog after Martin Buchholz's configure
               ;; mega-patch to r21.0-b34-pre2
               ((member "prog" (second opt))
                (build-configure-file cat opt)
                )
               ((member "VALUE" (second opt))
                (build-configure-string cat opt)
                )
               ((member "DIR" (second opt))
                (build-configure-dir cat opt)
                )
               ((member "LIB" (second opt))
                (build-configure-file cat opt)
                )
               ((member "PATH" (second opt))
                (build-configure-path cat opt)
                )
               ((or (null (second opt))
                    (subsetp (second opt)
                             '("no" "yes") :test 'string-equal))
                (build-configure-type cat opt)
                )
               (t
                (build-configure-type cat opt)
                )
               ))
            (delete-duplicates
             (cdr cat) :from-end t
             :test (lambda (a b)
                     (string=
                      (first a) (first b)))))))
   a-list))

(defun build-configure-process-option (option value detected doc category a-list)
  (let (prev-val prev-doc pos doc-vals)
    (unless (null value)
      (setq prev-val
            (first (cdr (assoc option (assoc category a-list)))))
      (setq prev-val
            (append prev-val (list value))))
    (setq detected
          (or
           (second (cdr (assoc option (assoc category a-list))))
           (null (null detected))))
    (setq prev-doc
          (third (cdr (assoc option (assoc category a-list)))))
    (unless (null doc)
      (setq prev-doc (append prev-doc (list doc)))
      (setq pos 0)
      (setq doc-vals (concat (first prev-doc)))
      (while (string-match "`\\(\\w+\\)'" doc pos)
        (setq prev-val
              (append prev-val (list (match-string 1 doc))))
        (setq pos (match-end 0)))
      (unless
          (null
           (string-match "\\([Vv]alid\\s-+types\\s-+are\\s-+\\|(\\)\\(\\(\\w+\\)\\(,\\s-*\\(\\(and\\|or\\)\\s-+\\)?\\(\\w+\\)\\)+\\)\\()\\|\\.\\)" doc 0))
        (setq doc-vals (match-string 2 doc))
        (setq pos 0)
        (while
            (string-match "\\(\\(,\\s-*\\(\\(and\\|or\\)\\s-+\\)?\\)?\\(\\w+\\)\\)" doc-vals pos)
          (setq prev-val
                (append prev-val (list (match-string 5 doc-vals))))
          (setq pos (match-end 0)))))
    (setcdr
     (assoc category a-list)
     (acons
      option
      (list prev-val detected prev-doc)
      (cdr (assoc category a-list))))))

(defun build-configure-generate (&optional file)
  (interactive "fconfigure.usage file: ")
  (unless file
    (setq file
          (expand-file-name
           "configure.usage"
           (cond
            ((string-equal build-from-what "Tarballs")
             (expand-file-name
              build-tarball-prefix
              build-tarball-dest))
            ((string-equal build-from-what "CVS")
             (expand-file-name
              build-cvs-checkout-dir
              build-cvs-checkout-parent-dir))))))
  (let
      (category categories option value detected doc build-configure-alist
                (buffer "build-configure.el"))
    (kill-buffer (get-buffer-create buffer))
    (with-output-to-temp-buffer buffer
      (save-window-excursion
        (find-file-read-only file)
        (build-configure-prolog file)
        (goto-char (point-min))
        (while (< (point) (point-max))
          (cond
           ((looking-at build-configure-option-paragraph)
            (goto-char (match-end 0))
            (build-configure-process-option
             (match-string 1)
             (match-string 3)
             (match-string 4)
             (match-string 5)
             category
             build-configure-alist))
           ((looking-at build-configure-option-category)
            (goto-char (match-end 0))
            (setq category (match-string 1))
            (setq build-configure-alist
                  (append build-configure-alist (list (list category)))))
           ;; We avoid matching a potentially zero-length string to
           ;; avoid infinite looping.
           ((looking-at
             "^.+$")
            (goto-char (match-end 0)))
           ((looking-at "\n")
            (goto-char (match-end 0)))))
        (build-configure-customize build-configure-alist)
;        (print build-configure-alist)
        ))
;    (set-buffer buffer)
;    (switch-to-buffer (get-buffer-create name))
    (kill-all-local-variables)
    (lisp-mode)
    (font-lock-mode 1)
    (toggle-read-only 1)))

(defun build-configure-string (cat opt)
  (princ (format "(defcustom build-configure%s\n" (first opt)))
  (princ "  \"\"\n")
  (princ (format "  %S\n" (build-configure-fill-doc (fourth opt))))
  (princ (format "  :group \'build-configure-%s\n" (first cat)))
  (princ "  :type '(string)\n")
  (princ "  :set 'build-configure-set-value)\n")
  (princ "\n"))

(defun build-configure-file (cat opt)
  (princ (format "(defcustom build-configure%s\n" (first opt)))
  (princ "  \"\"\n")
  (princ (format "  %S\n" (build-configure-fill-doc (fourth opt))))
  (princ (format "  :group \'build-configure-%s\n" (first cat)))
  (princ "  :type '(file)\n")
  (princ "  :set 'build-configure-set-value)\n")
  (princ "\n"))

(defun build-configure-dir (cat opt)
  (princ (format "(defcustom build-configure%s\n" (first opt)))
  (princ "  \"\"\n")
  (princ (format "  %S\n" (build-configure-fill-doc (fourth opt))))
  (princ (format "  :group \'build-configure-%s\n" (first cat)))
  (princ "  :type '(directory)\n")
  (princ "  :set 'build-configure-set-value)\n")
  (princ "\n"))

(defun build-configure-path (cat opt)
  (princ (format "(defcustom build-configure%s\n" (first opt)))
  (princ "  '()\n")
  (princ (format "  %S\n" (build-configure-fill-doc (fourth opt))))
  (princ (format "  :group \'build-configure-%s\n" (first cat)))
  (princ "  :type '(repeat\n")
  (princ "          :custom-show t\n")
  (princ "          :documentation-shown t\n")
  (princ "          (directory))\n")
  (princ "  :set 'build-set-path)\n")
  (princ "\n"))

(defun build-configure-types (cat opt)
  (princ (format "(defcustom build-configure%s\n" (first opt)))
  (princ (format "  '(%S)\n"
                 (if (third opt) "autodetected" "defaulted")))
  (princ (format "  %S\n" (build-configure-fill-doc (fourth opt))))
  (princ (format "  :group \'build-configure-%s\n" (first cat)))
  (princ "  :type '(choice\n")
  (if (third opt)
      (princ "          (const (\"autodetected\"))\n")
    (princ "          (const (\"defaulted\"))\n"))
  (princ "          (const (\"no\"))\n")
  (princ "          (set")
  (mapc (lambda (e)
          (princ (format "\n           (const %S)" e)))
        (set-difference
         (second opt)
         '("no" "TYPE[,TYPE]..." "TYPE")
         :test 'string=))
  (princ "))\n")
  (princ "  :set 'build-set-types)\n")
  (princ "\n"))

(defun build-configure-type (cat opt)
  (princ (format "(defcustom build-configure%s\n" (first opt)))
  (princ (format "  %S\n"
                 (if (third opt) "autodetected" "defaulted")))
  (princ (format "  %S\n" (build-configure-fill-doc (fourth opt))))
  (princ (format "  :group \'build-configure-%s\n" (first cat)))
  (princ "  :type '(choice\n")
  (if (third opt)
      (princ "          (const \"autodetected\")\n")
    (princ "          (const \"defaulted\")\n"))
  (princ "          (const \"no\")")
  (if (subsetp (second opt) '("no" "yes") :test 'string-equal)
      (princ "\n          (const \"yes\")")
    (mapc (lambda (e)
            (princ (format "\n          (const %S)" e)))
          (set-difference
           (second opt)
           '("no" "TYPE[,TYPE]..." "TYPE")
           :test 'string=)))
  (princ ")\n")
  (princ "  :set 'build-configure-set-value)\n")
  (princ "\n"))

(defun build-configure-fill-doc (doc)
  (with-temp-buffer
    (let ((sentence-end-double-space t)
          (use-hard-newlines t)
          (colon-double-space t))
      (insert (mapconcat 'eval doc "  "))
      (canonically-space-region (point-min) (point-max))
      (fill-region (point-min) (point-max))
      (goto-char (point-min))
      (while (re-search-forward "\\s-+\\'" nil t)
        (replace-match "" nil nil))
      (buffer-string))))

(defun build-configure-prolog (file)
  (princ ";;; Produced from
;;; ")
  (princ file)
  (princ "
;;; by ")
  (princ
   ;; Make sure the RCS keyword Id does not end up in the output file,
   ;; in case build.el is not `co -kv ...' or during development.
   (with-temp-buffer
     (insert build-version)
     (while (re-search-backward "\\$" nil t)
       (replace-match "" nil nil))
     (buffer-string)))
  (princ "\n;;; at\n;;; ")
  (princ (format-time-string "%a %b %d %T %Z %Y"))
  (princ "
(provide 'build-configure)\n
(setq build-configure-options nil)\n
(defun build-configure-sym-to-opt (sym)
  (substring (symbol-name sym) 15))\n
(defun build-set-path (sym val)
  (setq  build-configure-options
	 (acons (build-configure-sym-to-opt sym)
		(mapconcat '(lambda (item) item) val \":\")
		build-configure-options))
  (set-default sym val))\n
(defun build-set-types (sym val)
  (setq build-configure-options
	(acons (build-configure-sym-to-opt sym)
	       (mapconcat '(lambda (item) item) val \",\")
	       build-configure-options))
  (set-default sym val))\n
(defun build-configure-set-value (sym val)
  (setq build-configure-options
	(acons (build-configure-sym-to-opt sym) val
	       build-configure-options))
  (set-default sym val))\n
(defgroup build-configure nil
  \"XEmacs Build Configuration.\"
  :group 'build)\n
"))

;;}}}

;;{{{ Build CVS

(defgroup build-cvs nil
  "Standardizes the fetching of XEmacs from the CVS repository."
  :group 'build)

(defun build-cvs-checkout-options-validate (sym val)
  (cond
   ((string-match "-\\(d\\|N\\)\\b" val)
    (customize-set-value sym build-cvs-checkout-options)
    (warn "cannot use -d and -N.  `build-cvs-checkout-dir' will be used as -d argument if set, else `build-cvs-xemacs-module' will be used.  The -N option is unsupported."))
   (t
    (set-default sym val))))

(defcustom build-cvs-checkout-options
  "-P"
  "CVS checkout command-line options to use for all CVS commands."
  :set 'build-cvs-checkout-options-validate
  :type 'string
  :group 'build-cvs)

(defcustom build-cvs-options
  "-z3"
  "CVS command-line options to use for all CVS commands."
  :type 'string
  :group 'build-cvs)

(defcustom build-cvs-update-options
  "-P -d"
  "CVS update command-line options to use for all CVS commands."
  :type 'string
  :group 'build-cvs)

(defcustom build-cvs-checkout-parent-dir
  "/export/home/tmp/"
  "The parent directory on the local host into which the
`build-cvs-xemacs-module' will be checked out, named according to
`build-cvs-checkout-dir'."
  :type 'directory
  :group 'build-cvs)

(defcustom build-cvs-xemacs-module
  "xemacs"
  "CVS XEmacs module name to be checked out."
  :type 'string
  :group 'build-cvs)

(defcustom build-cvs-checkout-dir
  build-cvs-xemacs-module
  "The directory on the local host into which the
`build-cvs-xemacs-module' will be checked out. Be aware that cvs
checkout options -d and -N will affect the resulting directory
structure.  Therefor these options are disallowed in
`build-cvs-checkout-options'.  Please set `build-cvs-checkout-dir' if
you want to name the working direcory different from the default
`build-cvs-xemacs-module'.  The -N option is not supported, in order
to avoid unknown directory structures."
  :type 'string
  :group 'build-cvs)

(defcustom build-cvs-xemacs-release
  "release-21-2"
  "CVS XEmacs release to be checked out, if not checking out the
latest sources on the trunk."
  :type 'string
  :group 'build-cvs)

(defcustom build-cvs-xemacs-repository
  ":pserver:xemacs@cvs.xemacs.org:/usr/CVSroot"
  "CVS Repository where XEmacs can be checked out from."
  :type 'string
  :group 'build-cvs)

;;; BROKEN
(defun build-cvs-login ()
  "Login to XEmacs CVS repository."
  (interactive)
  (unless (file-exists-p build-cvs-checkout-parent-dir)
    (make-directory build-cvs-checkout-parent-dir t))
  (cd build-cvs-checkout-parent-dir)
  (let ((cmd
         (format "cvs %s -d%s login" build-cvs-options
                 build-cvs-xemacs-repository))
        (compilation-mode-hook
         'build-compilation-mode-hook)
        (compilation-buffer-name-function
         '(lambda (mode)
            (generate-new-buffer-name
             (concat build-cvs-checkout-dir "-cvs-login.err")))))
    (add-hook 'comint-mode-hook
              (function
               (lambda ()
                 (ring-insert-at-beginning comint-input-ring cmd))))
    (shell)))

(defun build-cvs-checkout (&optional release-tag)
  "Fetch XEmacs from the repository."
  (interactive "sXEmacs Release Tag: ")
  (unless (file-exists-p build-cvs-checkout-parent-dir)
    (make-directory build-cvs-checkout-parent-dir t))
  (cd build-cvs-checkout-parent-dir)
  (let ((cmd
         (format "cvs %s -d%s checkout %s -d %s%s %s"
                 build-cvs-options
                 build-cvs-xemacs-repository
                 build-cvs-checkout-options
                 build-cvs-checkout-dir
                 (if (and release-tag
                          (not (string-equal release-tag "")))
                     (concat " -r " release-tag)
                   "")
                 build-cvs-xemacs-module))
        (compilation-mode-hook
         'build-compilation-mode-hook)
        (compilation-buffer-name-function
         '(lambda (mode)
            (generate-new-buffer-name
             (format "%s-cvs-checkout%s.err" build-cvs-checkout-dir
                     (if (and release-tag
                              (not (string-equal release-tag "")))
                         (format "-%s" release-tag)
                       ""))))))
    (compile cmd)))

(defun build-cvs-update (&optional release-tag)
  "Update XEmacs from the repository to newest release or to release
specified by RELEASE-TAG'."
  (interactive "sXEmacs Release Tag: ")
  (cd
   (expand-file-name build-cvs-checkout-dir
                     build-cvs-checkout-parent-dir))
  (let ((cmd
         (format "cvs %s update %s%s"
                 build-cvs-options
                 build-cvs-update-options
                 (if (and release-tag
                          (not (string-equal release-tag "")))
                     (concat " -r " release-tag)
                   "")
                 ))
        (compilation-mode-hook
         'build-compilation-mode-hook)
        (compilation-buffer-name-function
         '(lambda (mode)
            (generate-new-buffer-name
             (concat build-cvs-checkout-dir "-cvs-update"
                     (when (and release-tag
                                (not (string-equal release-tag "")))
                       (format "-%s" release-tag))
                     ".err")))))
    (cond
     ((featurep 'pcl-cvs)
      (cvs-update    
       (expand-file-name build-cvs-checkout-dir
                         build-cvs-checkout-parent-dir)
       (split-string build-cvs-update-options "\\s-+")))
     (t
      (compile cmd)))))

;;}}}

;;{{{ Build From

(defvar build-from-what
  "Tarballs"
  "The Source Code units XEmacs is to be built from (\"Tarballs\" or
\"CVS\").")

(defun build-from-CVS ()
  (interactive)
  (let
      ((name
        (format "*Build XEmacs From Tarballs With %s*" build-with-what)))
    (kill-buffer (get-buffer-create name))
    (switch-to-buffer (get-buffer-create name))
    (kill-all-local-variables)
    (unless (file-exists-p build-cvs-checkout-parent-dir)
      (make-directory build-cvs-checkout-parent-dir t))
    (cd build-cvs-checkout-parent-dir)
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (switch-to-buffer "*Build XEmacs*"))
                   "Back")
    (widget-insert
     "\nYou need to customize CVS options and then download a release
of XEmacs.\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (customize-browse 'build-cvs))
                   "Browse Build CVS Options ...")
    (widget-insert "\n\n\t")
    (widget-apply
     (widget-create 'push-button
                    :notify (lambda (&rest ignore)
                              (build-cvs-login))
                    "CVS Login XEmacs")
     ;; build-cvs-login is not working!
     :deactivate)
    (widget-insert "\n	CVS Login XEmacs does not work.  Please do this from your favorite
	shell (once):
    \"cvs -d:pserver:xemacs@cvs.xemacs.org:/usr/CVSroot	login\"\n")
    (widget-insert "\n\t")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-cvs-checkout
                              build-cvs-xemacs-release))
                   "CVS Checkout XEmacs")
    (widget-insert "\n\n\t")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-cvs-update
                              build-cvs-xemacs-release))
                   "CVS Update XEmacs To Release")
    (widget-insert "\n\t")
    (widget-insert "Update to release "
                   build-cvs-xemacs-release
                   "\n")
    (widget-insert "\n\t")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-cvs-update))
                   "CVS Update XEmacs To Latest")
    (widget-insert "\n	Update to latest on the trunk.  Currently (2000-09-25) this is the
	21.1 release.

	")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (cond
                              ((string-equal build-with-what "GNU Tools")
                               (build-with-GNU
                                (expand-file-name
                                 build-cvs-checkout-dir
                                 build-cvs-checkout-parent-dir)))
                              ((string-equal build-with-what "Microsoft Tools")
                               (build-with-MS
                                (expand-file-name
                                 "nt"
                                 (expand-file-name
                                  build-cvs-checkout-dir
                                  build-cvs-checkout-parent-dir))))))
                   (format "Build XEmacs With %s Now ..." build-with-what))
    (use-local-map widget-keymap)
    (custom-mode)
    (widget-setup)
    (goto-char (point-min))))

(defun build-from-tarballs ()
  (interactive)
  (let
      ((name
        (format "*Build XEmacs From Tarballs With %s*" build-with-what)))
    (kill-buffer (get-buffer-create name))
    (switch-to-buffer (get-buffer-create name))
    (kill-all-local-variables)
    (cd build-tarball-dest)
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (switch-to-buffer "*Build XEmacs*"))
                   "Back")
    (widget-insert
     "\nYou need to customize Tarball options and then download a beta/release
version of XEmacs.\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (customize-browse 'build-tarball))
                   "Browse Build Tarball Options ...")
    (widget-insert "\n\t")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (dired build-tarball-site))
                   "Browse Build Tarball Site ...")
    (widget-insert "\n\t")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-tarball-expand-all))
                   "View Build Tarball Set ...")
    (widget-insert "\n\t")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-tarball-get-all))
                   "Download Build Tarball Set")
    (widget-insert "\n\t")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-tarball-extract-all))
                   "Install Downloaded Build Tarball Set")
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (cond
                              ((string-equal build-with-what "GNU Tools")
                               (build-with-GNU
                                (expand-file-name
                                 build-tarball-prefix
                                 build-tarball-dest)))
                              ((string-equal build-with-what "Microsoft Tools")
                               (build-with-MS
                                (expand-file-name
                                 "nt"
                                 (expand-file-name
                                  build-tarball-prefix
                                  build-tarball-dest))))))
                   (format "Build XEmacs With %s Now ..." build-with-what))
    (use-local-map widget-keymap)
    (custom-mode)
    (widget-setup)
    (goto-char (point-min))))

;;}}}

;;{{{ Build Tarballs

(defgroup build-tarball nil
  "Standardized the fetching of XEmacs beta/release tarballs."
  :group 'build)

(defcustom build-tarball-dest
  "/export/home/tmp/CVSroot/"
  "The destination directory on the local host the `build-tarball-set'
will be deposited in."
  :type 'directory
  :group 'build-tarball)

(defcustom build-tarball-dir
  "beta/xemacs-20.5/"
  "The sub-directory under `build-tarball-site' in which the
`build-tarball-set' are located."
  :type 'string
  :group 'build-tarball)

(defcustom build-tarball-prefix
  "xemacs-20.5-b32"
  "The prefix shared between all of the `build-tarball-set'.  This makes
it easy to switch over from one beta/release tarball set to the next,
e.g. from \"xemacs-20.4-b6\" to \"xemacs-20.4-b8\"."
  :type 'string
  :group 'build-tarball)

(defcustom build-tarball-set
  nil
  "The set of final name components of XEmacs tarballs you wish to
fetch."
  :type'(set
         (const "-elc.tar.gz")
         (const "-elc.tar.gz.sig")
         (const "-info.tar.gz")
         (const "-info.tar.gz.sig")
         (const "-mule.tar.gz")
         (const "-mule.tar.gz.sig")
         (const ".tar.gz")
         (const ".tar.gz.sig")
         (repeat
          :custom-show t
          :documentation-shown t
          (string "")))
  :group 'build-tarball)

(defcustom build-tarball-site
  "/ftp@ftp.xemacs.org:/pub/xemacs/"
  "The EFS path to a top-level XEmacs directory to fetch the XEmacs
`build-tarball-set' from."
  :type '(choice
          :custom-state t
          (const "/ftp@ftp.xemacs.org:/pub/xemacs/")
          (const "/ftp@ftp2.xemacs.org:/pub/xemacs/")
          (const "/ftp@ftp.ai.mit.edu:/pub/xemacs/")
          (const "/ftp@ftp.uu.net:/systems/gnu/xemacs/")
          (const "/ftp@ftp.sunet.se:/pub/gnu/xemacs/")
          (const "/ftp@ftp.cenatls.cena.dgac.fr:/pub/Emacs/xemacs/")
          (const "/ftp@ftp.th-darmstadt.de:/pub/editors/xemacs/")
          (const "/ftp@sunsite.doc.ic.ac.uk:/gnu/xemacs/")
          (const "/ftp@ftp.ibp.fr:/pub/emacs/xemacs/")
          (const "/ftp@uiarchive.cso.uiuc.edu:/pub/packages/xemacs/")
          (const "/ftp@ftp.technion.ac.il:/pub/unsupported/gnu/xemacs/")
          (const "/ftp@thphys.irb.hr:/pub/xemacs/")
          (const "/ftp@sunsite.cnlab-switch.ch:/mirror/xemacs/")
          (const "/ftp@ftp.unicamp.br:/pub/xemacs/")
          (const "/ftp@ftp.usyd.edu.au:/pub/Xemacs/")
          (const "/ftp@ftp.lab.kdd.co.jp:/xemacs/")
          (const "/ftp@SunSITE.sut.ac.jp:/pub/archives/packages/xemacs/")
          (const "/ftp@sunsite.icm.edu.pl:/pub/unix/xemacs/")
          (directory :tag "EFS Path" "/user@host.domain:/directory/"))
  :group 'build-tarball)

(defun build-tarball-expand (item)
  (let ((prfx
         (concat build-tarball-site build-tarball-dir build-tarball-prefix)))
    (concat prfx item)))

(defun build-tarball-collapse (item)
  (let ((str
         (concat build-tarball-site build-tarball-dir build-tarball-prefix)))
    (string-match str item)
    (replace-match "" t t item)))

(defun build-tarball-get (file)
  (if (not (featurep 'efs))
      (message
       "please install efs to be able to \"Download Build Tarball Set\".")
    (let (
          (efs-mode-hook
           '(lambda ()
              (set (make-local-variable 'efs-expire-ftp-buffers) nil)
              (set (make-local-variable 'auto-save-hook)
                   '(lambda ()
                      (message "Auto-saved %s\n" (buffer-name))))
              (auto-save-mode 1))))
      (efs-copy-file
       (build-tarball-expand file)
       (concat
        (expand-file-name
         build-tarball-prefix build-tarball-dest)
        file)
       1 nil t))))

(defun build-tarball-extract (file)
  (cd build-tarball-dest)
  (let ((cmd
         (format "gunzip -c %s%s | tar -xvf -" build-tarball-prefix file))
        (compilation-mode-hook
         'build-compilation-mode-hook)
        (compilation-buffer-name-function
         '(lambda (mode)
            (generate-new-buffer-name
             (concat
              (file-name-sans-extension
               (file-name-sans-extension
                (concat build-tarball-prefix file))) "-toc.err"))))
        )
    (if (string-match "tar\\.gz$" file)
        (compile cmd)
      (warn "%s is not a tar.gz file, skipped."
            (concat build-tarball-prefix file)))))

(defun build-tarball-get-all ()
  "Get all the expanded `build-tarball-set'.  Use `build-tarball-expand-all'
to find out which tarballs would be fetched by this function.  All
tarballs are saved under `build-tarball-dest'"
  (interactive)
  (mapc 'build-tarball-get build-tarball-set))

(defun build-tarball-extract-all ()
  "Extract all files from the locally present `build-tarball-set' which
have to be in \".tar.gz\" format."
  (interactive)
  (mapc 'build-tarball-extract build-tarball-set))

(defun build-tarball-expand-all ()
  "Print the expanded value of `build-tarball-set' to temporary buffer
\"Currently Selected Build Tarballs\"."
  (interactive)
  (cd build-tarball-dest)
  (with-output-to-temp-buffer
      "*Build Tarball Set*"
    (princ (mapconcat 'build-tarball-expand build-tarball-set "\n"))))

(defun build-tarball-add-url ()
  "Add URL near point to `build-tarball-set' via
`url-get-url-at-point'."
  (interactive)
  (setq build-tarball-set (cons (url-get-url-at-point) build-tarball-set)))

;;}}}

;;{{{ Build

(defgroup build nil
  "Simplifies Building XEmacs; i.e. Fetching, Configuring, Making, and
Reporting."
  :link '(url-link :tag "XEmacs Beta README"
                   "ftp://ftp@ftp.xemacs.org/pub/xemacs/beta/README")
  :link '(url-link :tag "XEmacs README"
                   "ftp://ftp@ftp.xemacs.org/pub/xemacs/README")
  :group 'emacs)

;;;###autoload
(defun build ()
  "Creates a widget-based interface to build a beta/release version of
XEmacs.  All aspects of fetching tarballs, configuring, making and
reporting can be customized and executed from the newly created buffer
*Build*."
  (interactive)
  (let
      (
;        build-from-cvs-button-widget
;        build-from-tarballs-button-widget
       (name "*Build XEmacs*"))
    (kill-buffer (get-buffer-create name))
    (switch-to-buffer (get-buffer-create name))
    (kill-all-local-variables)
    (widget-insert "\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (Info-query "build"))
                   "Visit Build Info")
    (widget-insert "\nVisit online information documenting the build package.

")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (customize-browse 'build))
                   "Browse Build Options ...")
    (widget-insert "\nBrowse and customize any options of the build process according to
your current choices for the sources to build from and the tools to
build with.

")
    (widget-create 'choice
                   :tag "Build from"
                   :value build-from-what
                   :notify (lambda (widget &rest ignore)
                             (setq build-from-what (widget-value widget))
                             (cond
                              ((string-equal build-from-what "CVS")
                               (widget-apply 
                                build-from-cvs-button-widget
                                :activate)
                               (widget-apply 
                                build-from-tarballs-button-widget
                                :deactivate))
                              ((string-equal build-from-what "Tarballs")
                               (widget-apply 
                                build-from-cvs-button-widget
                                :deactivate)
                               (widget-apply 
                                build-from-tarballs-button-widget
                                :activate))))
                   '(item :value "CVS")
                   '(item :value "Tarballs"))
    (widget-insert
     "To build a beta/release version of XEmacs please decide first whether
to build from tarballs in .tar.gz format or from CVS sources.")
    (widget-insert "\n\n")
    (widget-create 'choice
                   :tag "Build with"
                   :value build-with-what
                   :notify (lambda (widget &rest ignore)
                             (setq build-with-what (widget-value widget)))
                   '(item :value "GNU Tools")
                   '(item :value "Microsoft Tools")
                   )
    (widget-insert
     "Furthermore, please specify whether you will build with GNU tools
using configure and make or Microsoft Tools using nt\xemacs.mak and
VC++ 4.0 or higher.

")
    (widget-insert "\n\n")
    (setq
     build-from-cvs-button-widget
     (widget-create 'push-button
                    :notify (lambda (&rest ignore)
                              (build-from-CVS))
                    "Build XEmacs From CVS Now"))
    (widget-insert "\n\n")
    (setq
     build-from-tarballs-button-widget
     (widget-create 'push-button
                    :notify (lambda (&rest ignore)
                              (build-from-tarballs))
                    "Build XEmacs From Tarballs Now"))
    ;; Initialize these buttons according to `build-from-what'.
    (cond
     ((string-equal build-from-what "CVS")
      (widget-apply 
       build-from-cvs-button-widget
       :activate)
      (widget-apply 
       build-from-tarballs-button-widget
       :deactivate))
     ((string-equal build-from-what "Tarballs")
      (widget-apply 
       build-from-cvs-button-widget
       :deactivate)
      (widget-apply 
       build-from-tarballs-button-widget
       :activate)))
    (widget-insert
     "\nProceed after you have chosen what sources to build from and what
tools to build with.
")
    (use-local-map widget-keymap)
    (custom-mode)
    (widget-setup)
    (goto-char (point-min))))

;;}}}

;;{{{ Build With

(defvar build-with-what
  "GNU Tools"
  "The Toolset XEmacs is to be built with (\"GNU Tools\" or
\"Microsoft Tools\").")

(defgroup build-with-MS nil
  "Standardizes the building of XEmacs with MiroSoft tools."
  :group 'build)

(defcustom build-with-MS-make-command
  "nmake"
  "Path of Microsoft make utility used to build XEmacs."
  :type 'file
  :group 'build-with-MS)

(defcustom build-with-MS-make-options
  nil
  "Options to use with Microsoft make utility when building XEmacs."
  :type '(repeat string)
  :group 'build-with-MS)

(defun build-with-GNU (dir)
  (interactive)
  (let
      ((name "*Build XEmacs With GNU Tools*"))
    (kill-buffer (get-buffer-create name))
    (switch-to-buffer (get-buffer-create name))
    (kill-all-local-variables)
    (cd dir)
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (switch-to-buffer "*Build XEmacs*"))
                   "Back")
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (message (pwd))
                             (build-configure-generate "configure.usage"))
                   "Generate Build Configure")
    (widget-insert "\n\t")
    (widget-apply
     (widget-create 'push-button
                    :notify (lambda (&rest ignore)
                              (eval-buffer "build-configure.el"))
                    "Activate Generated Build Configure")
     (if (boundp 'build-configure-options)
         :deactivate
       :activate))
    (when (boundp 'build-configure-options)
      (widget-insert
       "\n	You will need to restart XEmacs first if you want to activate the
	generated interface to Build Make again.

"))
    (widget-insert "\n\t")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (customize-browse 'build-configure))
                   "Customize Build Configure ...")
    (widget-insert "\n\t")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-configure))
                   "Run XEmacs Configure")
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-make-generate))
                   "Generate XEmacs Make")
;    (widget-insert "\n\t")
;    (widget-create 'push-button
;		   :notify (lambda (&rest ignore)
;			     (customize-browse 'build-make))
;		   "Customize Build-Make")
    (widget-insert "\n\t")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (call-interactively 'build-make))
                   "Run XEmacs Make")
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-build-report))
                   "Generate XEmacs Build Report ...")
    (widget-insert "\n\n")
    (use-local-map widget-keymap)
    (custom-mode)
    (widget-setup)
    (goto-char (point-min))))

(defun build-with-MS (dir)
  (interactive "DXEmacs source directry: ")
  (let
      ((name "*Build XEmacs With Microsoft Tools*"))
    (kill-buffer (get-buffer-create name))
    (switch-to-buffer (get-buffer-create name))
    (kill-all-local-variables)
    (cd (expand-file-name "" dir))
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (switch-to-buffer "*Build XEmacs*"))
                   "Back")
    (widget-insert "\n")
    (widget-insert
     "\nYou need to customize Microsoft Tools options.\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (customize-browse 'build-with-MS))
                   "Browse Build With MS Options ...")
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-make-generate "xemacs.mak"))
                   "Generate XEmacs Make")
    (widget-insert "\n\t")
    (widget-apply
     (widget-create 'push-button
                    :notify (lambda (&rest ignore)
                              (eval-buffer "build-make.el"))
                    "Activate Generated Build Make")
     (if (boundp 'build-make-options)
         :deactivate
       :activate)
     )
    (when (boundp 'build-make-options)
      (widget-insert
       "\n\tYou will need to restart XEmacs to activate
	the generated interface to Build Make.\n\n"))
    (widget-insert "\n\t")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (customize-browse 'build-make))
                   "Customize Build Make ...")
;;; APA: config.inc file was introduced by Ben Wing in 21.2-b32.
    (multiple-value-bind
        (major minor beta codename)
        (build-report-version-file-data build-report-version-file)
      (when
          (and
           (>= (string-to-int major) 21)
           (or
            ;; 21.2 versions >= b32
            (and
             (= (string-to-int minor) 2)
             (>= (string-to-int beta) 32))
            ;; 21 versions with minor number > 2
            (> (string-to-int minor) 2)))
        (widget-insert "\n\t")
        (widget-create 'push-button
                       :notify (lambda (&rest ignore)
                                 (build-config-inc-generate))
                       "Generate config.inc")))
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-make
                              "distclean"
                              (mapconcat
                               'identity
                               (cons
                                build-with-MS-make-command
                                build-with-MS-make-options)
                               " ")))
                   "Clean XEmacs Distribution")
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-make
                              "all"
                              (concat
                               (mapconcat
                                'identity
                                (cons
                                 build-with-MS-make-command
                                 build-with-MS-make-options)
                                " ")
                               (build-make-get-option-string))))
                   "Build XEmacs")
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-make
                              "install"
                              (concat
                               (mapconcat
                                'identity
                                (cons
                                 build-with-MS-make-command
                                 build-with-MS-make-options)
                                " ")
                               (build-make-get-option-string))))
                   "Build and Install XEmacs")
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-make
                              "check-temacs"
                              (concat
                               (mapconcat
                                'identity
                                (cons
                                 build-with-MS-make-command
                                 build-with-MS-make-options)
                                " ")
                               (build-make-get-option-string))))
                   "Check temacs (XEmacs before dumping)")
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-make
                              "check"
                              (concat
                               (mapconcat
                                'identity
                                (cons
                                 build-with-MS-make-command
                                 build-with-MS-make-options)
                                " ")
                               (build-make-get-option-string))))
                   "Check XEmacs")
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (build-build-report))
                   "Generate XEmacs Build Report ...")
    (widget-insert "\n\n")
    (use-local-map widget-keymap)
    (custom-mode)
    (widget-setup)
    (goto-char (point-min))))

(defun build-with-MS-make-commandline ()
  "Internal function building MS make commandline."
  (concat
   (mapconcat
    'identity
    (cons
     build-with-MS-make-command
     build-with-MS-make-options)
    " ")
;;; APA: config.inc file was introduced by Ben Wing in 21.2-b32.
;;; use commandline options for older XEmacs version
   (multiple-value-bind
       (major minor beta codename)
       (build-report-version-file-data build-report-version-file)
     (when
         (and
          (>= (string-to-int major) 21)
          (>= (string-to-int minor) 2)
          (or (null beta)
              (>= (string-to-int beta) 32))) ; >= 21.2 beta
       (build-make-get-option-string)))))

;;}}}

;;{{{ Build Report

(defun build-build-report ()
  (interactive)
  (let
      ((name "*Generate XEmacs Build Report*"))
    (kill-buffer (get-buffer-create name))
    (switch-to-buffer (get-buffer-create name))
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (switch-to-buffer "*Build XEmacs*"))
                   "Back")
    (widget-insert "\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (customize-browse 'build-report))
                   "Customize Build Report ...")
    (widget-insert "\nYou may need to customize Build Report options in order to find all
information created by your last building of XEamcs.\n\n")
    (widget-create 'push-button
                   :notify (lambda (&rest ignore)
                             (call-interactively 'build-report))
                   "Generate Build Report ...")
    (widget-insert "\n")
    (use-local-map widget-keymap)
    (custom-mode)
    (widget-setup)
    (goto-char (point-min))))

;;}}}

;;{{{ Build Make

(defvar build-make-alist
  nil
  "Internal variable keeping track of makefile macros and tragets")

(defconst build-make-target-doc-paragraph
  "^##\\s-*make\\s-+\\([^
 	]+\\(\\s-+or\\s-+make\\s-+\\([^
 	]+\\)\\)*\\)\\(\\s-*\\(\\(.*\\)\\(\n##\\s-\\{3,\\}.+\\)*\\)\\)$"
  "REGEXP matching a XEmacs makefile target comment.  These comments
don't exist in `xemacs.mak'")

(defconst build-make-target-paragraph
  "\\(^#.+
\\)?\\(\\(\\w\\|_\\)+\\)\\s-*:.*"
  "REGEXP matching a XEmacs makefile target name.")

(defconst build-make-macro-paragraph
  "^\\(\\(\\w\\|_\\)+\\)\\s-*=\\s-*\\(\\(.*\\\\
\\)*.+\\)$"
;;;  makefile-macroassign-regex
  "REGEXP matching a XEmacs makefile macro definition.")

(defconst build-make-prolog
  "
(provide 'build-make)

(setq build-make-options nil)

(defun build-make-sym-to-opt (sym)
  (substring (symbol-name sym) 11))

(defun build-make-set-value (sym val)
  (cond
   ((equal val (first (get sym 'standard-value)))
    (setq build-make-options
          (remassoc (build-make-sym-to-opt sym) build-make-options)))
   (t
    (setq build-make-options
	  (acons (build-make-sym-to-opt sym) val
		 build-make-options))))
  (set-default sym val))

(defgroup build-make nil
  \"build-make options.\"
  :group 'build)

"
  "Internal variable of `build'.")

(defun build-config-inc-generate (&optional dir)
  (interactive)
  (let
      ((buffer (buffer-name (generate-new-buffer "config.inc"))))
    (if dir
        (cd dir))
    (with-output-to-temp-buffer buffer
      (save-window-excursion
        (princ "# -*- mode: makefile -*-\n")
        (princ (format "# generated by %s" build-version))
        (princ "\n\n")
        (princ
         (if (boundp 'build-make-options)
             (mapconcat
              (function (lambda (e)
                          (cond
                           (t
                            (format "%s=%s\n" (first e) (rest e))))))
              (sort
               (delete-duplicates
                build-make-options :from-end t
                :test (lambda (a b)
                        (string=
                         (first a) (first b))))
               (lambda (a b)
                 (string<
                  (first a) (first b))))
              "")
           ""))
        ))
    (set-buffer buffer)
    (kill-all-local-variables)
    (makefile-mode)
    (font-lock-mode 1)
    (toggle-read-only 1)))

(defun build-make (&optional target command)
  "Build the XEmacs target argument according to the settings in
customized group `build' and its members."
  (interactive "sTarget: \nsCommand: ")
  (let ((cmd
         (if (string-equal command "")
             (format "make %s" target)
           (format "%s %s" command target)))
        (compilation-mode-hook
         'build-compilation-mode-hook)
        (compilation-buffer-name-function
         '(lambda (mode)
            (generate-new-buffer-name
             (format "%s-make%s.err"
                     (cond
                      ((string-equal build-from-what "Tarballs")
                       build-tarball-prefix)
                      ((string-equal build-from-what "CVS")
                       build-cvs-checkout-dir))
                     (if target
                         (format "-%s" target)
                       ""))))))
    (compile cmd)))

(defun build-make-generate (&optional file)
  (interactive "fMakefile: ")
  (setq build-make-alist (list (cons 'macros nil) (cons 'targets nil)))
  (unless file
    (setq file
          (expand-file-name
           "Makefile.in"
           (cond
            ((string-equal build-from-what "Tarballs")
             (expand-file-name
              build-tarball-prefix
              build-tarball-dest))
            ((string-equal build-from-what "CVS")
             (expand-file-name
              build-cvs-checkout-dir
              build-cvs-checkout-parent-dir))))))
  (let
      (category categories option value detected doc
                (buffer "build-make.el"))
    (with-output-to-temp-buffer buffer
      (save-window-excursion
        (find-file-read-only file)
;	(build-make-prolog file)
        (goto-char (point-min))
        (while (< (point) (point-max))
          (cond
           ((looking-at build-make-target-doc-paragraph)
            (goto-char (match-end 0))
            (build-make-process-target-doc
             ;; target [or target ...]
             (match-string 1)
             ;; documentation for current targets; possibly
             ;; spreading multiple lines.
             (match-string 5)
             build-make-alist))
           ((looking-at build-make-target-paragraph)
            (goto-char (match-end 0))
            (when (> (length (match-string 1)) 0)
              (build-make-process-target-doc
               ;; target name
               (match-string 2)
               ;; documentation for target; possibly
               ;; spreading multiple lines.
               (match-string 1)
               build-make-alist))
            )
           ((looking-at build-make-macro-paragraph)
            (goto-char (match-end 0))
;	    (unless (string-match "\\$" (match-string 3))
            (build-make-process-macro
             ;; macro name
             (match-string 1)
             ;; macro value
             (match-string 3)
             build-make-alist))
;	    )
           ((looking-at
             "^.+$")
            (goto-char (match-end 0)))
           ((looking-at "\n")
            (goto-char (match-end 0)))
           ))
        (build-make-customize build-make-alist)
        ))
    (set-buffer buffer)
    (insert "(setq build-make-alist (quote")
;    (cl-prettyprint (nreverse build-make-alist))
    (cl-prettyprint build-make-alist)
    (insert "))\n")
    (toggle-read-only 1)))

(defun build-make-get-option-string ()
  (if (boundp 'build-make-options)
      (mapconcat
       (function (lambda (e)
                   (cond
                    (t
                     (format " %s=\"%s\"" (first e) (rest e))))))
       (delete-duplicates
        build-make-options :from-end t
        :test (lambda (a b)
                (string=
                 (first a) (first b))))
       "")
    ""))

(defun build-make-process-target-doc (targets doc a-list)
  (setq targets (replace-in-string targets "or\\(\n\\|\\s-\\)+make" ""))
  (setq doc (replace-in-string doc "##?\\s-+" ""))
  (setq doc (build-configure-fill-doc (list doc)))
  (setcdr (assoc 'targets a-list)
          (append (list (list targets doc)) (cdr (assoc 'targets a-list)))))

(defun build-make-process-macro (name value a-list)
  (unless (assoc name (assoc 'macros a-list))
    (setcdr (assoc 'macros a-list)
            (append (list (list name value)) (cdr (assoc 'macros a-list))))))

(defun build-make-customize (a-list)
  (princ build-make-prolog)
  (cond
   ((string-equal build-with-what
                  "GNU Tools")
    nil)
   ((string-equal build-with-what
                  "Microsoft Tools")
    (mapcar
     (lambda (macro)
       (build-make-file (first macro) (second macro))
       )
     (list
      (list "COMPFACE_DIR" "")
      (list "GTK_DIR" "")
      (list "JPEG_DIR" "")
      (list "PNG_DIR" "")
      (list "TIFF_DIR" "")
      (list "XPM_DIR" "")
      (list "ZLIB_DIR" "")))))
  (mapcar
   (lambda (macro)
     (build-make-string (first macro) (second macro))
     )
   (rest (assoc 'macros a-list))))

(defun build-make-string (name val)
  (princ (format "(defcustom build-make-%s\n" name))
  (princ (format "  %S\n" val))
  (princ (format "  \"macro %s\"\n" name))
  (princ (format "  :group \'build-make\n"))
  (princ "  :type 'string\n")
  (princ "  :set 'build-make-set-value)\n")
  (princ "\n"))

(defun build-make-file (name val)
  (princ (format "(defcustom build-make-%s\n" name))
  (princ (format "  %S\n" val))
  (princ (format "  \"macro %s\"\n" name))
  (princ (format "  :group \'build-make\n"))
  (princ "  :type 'file\n")
  (princ "  :set 'build-make-set-value)\n")
  (princ "\n"))

;;}}}

;; build.el ends here
