13

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.

3
  • If you are using projectile there are ways to compile from projectile-project-root. For instance, github.com/abo-abo/helm-make
    – abo-abo
    Jan 17, 2015 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. Jan 17, 2015 at 17:20
  • thanks, projectile-compile-project works for me
    – netawater
    Jan 15, 2019 at 6:05

3 Answers 3

21

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"))))
5
  • 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, 2015 at 20:32
  • Nice! vim has a similar function named findfile, I was surprised emacs didn't have it. Jan 17, 2015 at 21:00
  • Isn't there something similar to lisp's let? You're running locate-dominating-file twice. Jan 17, 2015 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, 2015 at 4:50
  • I wish I could up vote this answer more. Thank you.
    – Erik
    Aug 30, 2016 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"))))
7
  • Awesome! This is exactly what I wanted. Jan 17, 2015 at 18:20
  • OK, just be careful with the end condition of the loop. If you're on Windows, you might get an endless loop.
    – abo-abo
    Jan 17, 2015 at 18:22
  • Side question: why are you using with-temp-buffer? Jan 17, 2015 at 18:23
  • Yeah I don't care about windows, but thanks for mentioning it. Jan 17, 2015 at 18:23
  • 1
    You could have simply bound default-directory.
    – jch
    Jan 17, 2015 at 20:45
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)))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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