2

I'm trying to map F5 to compile from a parent directory of the current buffer.

Emacs compile-command find makefile in superior directory provides an excellent answer to achieve this:

(defun compile-project ()
  (interactive)
  (let* ((make-directory (locate-dominating-file (buffer-file-name)
                                                  "Makefile"))
          (command (concat "make -k -C "
                           (shell-quote-argument make-directory))))
  (compile command)))

(global-set-key (kbd "<f5>") 'compile-project)

Besides, C/C++ Development Environment for Emacs describes a way such that F5 does not prompt for the compile command every time, but if a prefix argument C-u is provided, then compile prompts for a compile command:

(global-set-key (kbd "<f5>") (lambda ()
                               (interactive)
                               (setq-local compilation-read-command nil)
                               (call-interactively 'compile)))

Question: How can I modify the compile-project function, so that when a prefix command C-u is provided, it shows the compilation command in the mini-buffer, including a default command of make -k -C ... generated by the body of let*?

1

I think that you almost had it. I modified your original compile-project as follows:

(defun compile-project ()
  (interactive)
  (let* ((mk-dir (locate-dominating-file (buffer-file-name) "Makefile"))
         (compile-command (concat "make -k -C " (shell-quote-argument mk-dir)))
         (compilation-read-command nil))
    (call-interactively 'compile)))

The only real difference is that the compile function reads from compilation-read-command to figure out how to read the compilation command and compile-command. If you set compilation-read-command to nil, compile will use the value of compile-command instead of prompting.

(Also, all of this is available in M-x describe-function compile if you need to look up something more.)

0

This is an expanded version of @Xaldew's answer that...

  • Checks for multiple kinds of makefile.
  • Reports when the makefile isn't found.
(defun locate-dominating-file-multi (dir compilation-filenames)
  "Search for the compilation file traversing up the directory tree.

DIR the base directory to search.
COMPILATION-FILENAMES a list pairs (id, list-of-names).
Note that the id can be any object, this is intended to identify the kind of group.

Returns a triplet (dir, filename, id) or nil if nothing is found.
"
  ;; Ensure 'test-dir' has a trailing slash.
  (let
    (
      (test-dir (file-name-as-directory dir))
      (parent-dir (file-name-directory (directory-file-name dir)))
      )
    (catch 'mk-result
      (while (not (string= test-dir parent-dir))
        (dolist (test-id-and-filenames compilation-filenames)
          (cl-destructuring-bind (test-id test-filenames) test-id-and-filenames
            (dolist (test-filename test-filenames)
                (when (file-readable-p (concat test-dir test-filename))
                (throw 'mk-result (list test-dir test-filename test-id))
                )
              )
            )
          )
        (setq
          test-dir parent-dir
            parent-dir (file-name-directory (directory-file-name parent-dir))
          )
        )
      )
    )
  )

(defun compile-from-any-makefile ()
  (interactive)
  (let*
    (
      (mk-reference-dir (expand-file-name "."))
      (mk-dir-file-id
        (locate-dominating-file-multi
          mk-reference-dir
          (list
            '("make" ("Makefile" "makefile" "GNUmakefile"))
            '("ninja" ("build.ninja"))
            '("scons" ("SConstruct"))
            )
          )
        )
      )
    (if mk-dir-file-id
      (cl-destructuring-bind (dir file id) mk-dir-file-id
        (let
          (
            ;; ensure 'compile-command' is used.
            (compilation-read-command nil)
            (compile-command
              (cond
                ((string= id "make")
                  (concat "make -C " (shell-quote-argument dir)))
                ((string= id "ninja")
                  (concat "ninja -C " (shell-quote-argument dir)))
                ((string= id "scons")
                  (concat "scons -C " (shell-quote-argument dir)))
                (t
                  (error "Unhandled type (internal error)"))
                )
              )
            )
          (call-interactively 'compile)
          )
        )
      (message "No makefile found in %S" mk-reference-dir)
      )
    )
  )

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.