; Stolfi's procedures for compilation within emacs. ; Last edited on 2019-08-13 18:50:49 by stolfilocal (defvar compile-makefile-name nil "Full name of the \"makefile\" to use when compiling this buffer." ) (make-variable-buffer-local 'compile-makefile-name) (make-variable-buffer-local 'compile-command) (defun stolfi-makefile-name-from-buffer-name () "If the current buffer is visiting a file, returns that file's name without the directory and with the extension replaced by \".make\" . Otherwise returns nil." (if (buffer-file-name) (concat (file-name-sans-extension (file-name-nondirectory (buffer-file-name))) ".make") nil ) ) (defun stolfi-get-makefile-name (mkf) "If `mkf' is not nil and names a readable file, returns `mkf' itself. Otherwise, if the current directory contains a readable file called \"FOO.make\" where FOO is the current buffer's name minus the extension, or \"Makefile\", or \"makefile\", returns that file's name. Otherwise, if the parent or grand-parent of the current directory contains a file called \"Makefile\", or \"makefile\", returns that file's name. Otherwise returns nil." (let* ( (dir default-directory) ) (or ; Try the given file `mkf': (stolfi-file-name-if-readable mkf dir) ; Try "FOO.make" where FOO is the current buffer's file name: (stolfi-file-name-if-readable (stolfi-makefile-name-from-buffer-name) dir) ; Try "Makefile" and "makefile" in the current directory: (stolfi-file-name-if-readable "Makefile" dir) (stolfi-file-name-if-readable "makefile" dir) ; Try "Makefile" and "makefile" in the parent directory: (progn (setq dir (concat dir "../")) nil) (stolfi-file-name-if-readable "Makefile" dir) (stolfi-file-name-if-readable "makefile" dir) ; Try "Makefile" and "makefile" in the grandparent directory: (progn (setq dir (concat dir "../")) nil) (stolfi-file-name-if-readable "Makefile" dir) (stolfi-file-name-if-readable "makefile" dir) ) ) ) (defun stolfi-fix-compile-command (cmd mkf) "If `cmd' and `mkf' are not nil and `cmd' contains the substring \" -f ANYNAME\", where ANYNAME is any non-blank string, returns `cmd' with that substring replaced by \" -f MKFNAME\" where MKFNAME is the value of `mkf' without the directory part. If `cmd' alone is nil, returns \"make -f MKFNAME all\". If `mkf' alone is nil, simply removes the substring \" -f ANYNAME\" from `cmd', if present. If both are nil, returns \"make all\"." (cond ( (and (null cmd) (null mkf)) "make all" ) ( (null cmd) (concat "make -f " (file-name-nondirectory mkf)) ) ( (string-match "[ ]+-f[ ]+[^ ]+[ ]*" cmd) (concat (substring cmd 0 (match-beginning 0)) (if (not mkf) " " (concat " -f " (file-name-nondirectory mkf) " ") ) (substring cmd (match-end 0)) ) ) ( (not cmd) (concat "make -f " mkf " all") ) ( mkf (concat cmd " -f " mkf " all") ) (t cmd) ) ) (defun stolfi-recompile-buffer (&optional arg) "Tries to (re)compile the program which includes the current buffer.\n\nThe function first determines a makefile that is appropriate for this buffer, using `stolfi-get-makefile-name'. This search may or may not define the buffer-local variables `compile-makefile-name' and `compile-command'. Then the function connects to the directory containing `compile-makefile-name' and executes `(compile compile-command)'.\n\nThe makefile and/or the command are prompted for, if necessary. They are always prompted for when the command is executed with a non-zero, non-nil argument. [stolfi]" (interactive "P") (let* ( (mkf (stolfi-get-makefile-name compile-makefile-name)) (mkf-changed (and compile-makefile-name (not (equal mkf compile-makefile-name)))) (prompt-mkf (or arg (not mkf) mkf-changed)) ) ; (message "(1) arg = %S" arg) ; (message "(1) compile-makefile-name = %S" compile-makefile-name) ; (message "(1) mkf = %S" mkf) ; (message "(1) mkf-changed = %S" mkf-changed) ; (message "(1) prompt-mkf = %S" prompt-mkf) (if mkf-changed (setq compile-makefile-name mkf)) ; Confirm the value of `compile-makefile-name', if needed: (if prompt-mkf (progn (setq mkf (read-string "Makefile: " mkf)) (setq compile-makefile-name mkf) ) ) ; Now the makefile name is ok. Get the "make" command: (let* ( (cmd (stolfi-fix-compile-command compile-command mkf)) (cmd-changed (and compile-command (not (equal cmd compile-command)))) (prompt-cmd (or arg (not cmd) cmd-changed)) ) ; (message "(1) compile-command = %S" compile-command) ; (message "(1) cmd = %S" cmd) ; (message "(1) cmd-changed = %S" cmd-changed) ; (message "(1) prompt-cmd = %S" prompt-cmd) ; Fix the compile command if necessary. (if cmd-changed (setq compile-command cmd)) ; Confirm the value of `compile-command', if needed: (if prompt-cmd (progn (setq cmd (read-string "Compile command: " cmd)) (setq compile-command cmd) ) ) ; Connect to the makefile's directory and run it: (let* ( (cur-dir default-directory) (mkf-dir (file-name-directory mkf)) ) (if (not mkf-dir) (setq mkf-dir cur-dir)) (message "compiling in directory \"%s\" with \"%s\"" mkf-dir cmd) (cd-absolute mkf-dir) ; Kill any current compilation: (if (buffer-live-p (get-buffer "*compilation*")) (progn (message "killing compilation...") (condition-case nil (kill-process (get-buffer "*compilation*")) (error nil)) ) ) (compile cmd) (cd-absolute cur-dir) ) ) ) )