12

The problem with M-x compile is that if the Makefile isn't in the current directory, it fails.

I'd like to have a function that recursively goes up to find a Makefile and run make from this directory.

I've seen this question, but it's path-specific, and it's something I have to think about running.

  • If you are using `projectile` there are ways to compile from `projectile-project-root`. For instance, https://github.com/abo-abo/helm-make – abo-abo Jan 17 '15 at 17:00
  • @abo-abo I hadn't tried projectile! It seems that `projectile-compile-project` does what I want. But I'd like to have something without this dependency, if possible. – Florian Margaine Jan 17 '15 at 17:20
  • thanks, projectile-compile-project works for me – netawater Jan 15 '19 at 06:05

3 Answers3

20

You are looking for the function locate-dominating-file. Here is the emacs documentation for this function:

(locate-dominating-file FILE NAME)

Look up the directory hierarchy from FILE for a directory containing NAME. Stop at the first parent directory containing a file NAME, and return the directory. Return nil if not found. Instead of a string, NAME can also be a predicate taking one argument (a directory) and returning a non-nil value if that directory is the one for which we're looking.

Using this, abo-abo's answer can be shortened to

(defun desperately-compile ()
  "Traveling up the path, find a Makefile and `compile'."
  (interactive)
  (when (locate-dominating-file default-directory "Makefile")
  (with-temp-buffer
    (cd (locate-dominating-file default-directory "Makefile"))
    (compile "make -k"))))
Pradhan
  • 2,330
  • 14
  • 28
  • Nice! This function does a similar thing to my loop, but in a well-documented and edge-case handling way. I'll make sure to add it to my recipe list, thanks. – abo-abo Jan 17 '15 at 20:32
  • Nice! vim has a similar function named [`findfile`](http://vimdoc.sourceforge.net/htmldoc/eval.html#findfile()), I was surprised emacs didn't have it. – Florian Margaine Jan 17 '15 at 21:00
  • Isn't there something similar to lisp's `let`? You're running `locate-dominating-file` twice. – Florian Margaine Jan 17 '15 at 21:24
  • @FlorianMargaine Yes, you could use let. I was typing the answer in directly without testing and figured this was less likely to have misplaced parentheses :) – Pradhan Jan 18 '15 at 04:50
  • I wish I could up vote this answer more. Thank you. – Erik Aug 30 '16 at 20:53
6

Recursive compile, no dependencies attached:

(defun desperately-compile ()
  "Traveling up the path, find a Makefile and `compile'."
  (interactive)
  (with-temp-buffer
    (while (and (not (file-exists-p "Makefile"))
                (not (equal "/" default-directory)))
      (cd ".."))
    (when (file-exists-p "Makefile")
      (compile "make -k"))))
abo-abo
  • 13,943
  • 1
  • 29
  • 43
0

Here's a version using let and interactively editable command:

(defun make-project (cmd)
  "Traveling up the path, find a Makefile and `make'."
  (interactive (list (read-string "make command: " "make -k")))
  (let ((make-dir (locate-dominating-file default-directory "Makefile")))
    (when make-dir
      (with-temp-buffer
        (cd make-dir)
        (compile cmd)))))
Jay Doane
  • 101
  • 2