views:

953

answers:

9

I have a makefile in the project root directory. If I am editing a file in a subdirectory, how do I invoke make from EMACS? M-x compile make will not work as it looks for the makefile in the current directory. But I have the makefile in the project root directory.

Any thoughts?

Edit

As suggested, *make -f fullpath_to_makefile* did the trick. But I have some includes in the makefile like include "tests/module.mk" which failed. It is looking for "tests" directory in the subdirectory. This can be solved by specifying fully qualified path in the makefile. But I don't think that is a good solution. Any better approaches?

+1  A: 

Hi

When I use Emacs to compile the minibuffer looks something like this;

make -k

and I just add any text I like, such as -f ../../root/Makefile. This might work for you.

Mark

High Performance Mark
The -f switch did the trick. But in the makefile, I have some includes like "include tests/module.mk" and it complains that it can't find "tests" directory. It is looking for "tests" in the subdirectory. So looks like I need to use fully qualified path everywhere. Thanks for the reply.
Appu
+1  A: 

I'm not much of an emacs user, but could you pass make -C ../ or however many directories up you need to go?

opello
This will run make in the root directory. Appu wants to run make in the local directory, using the makefile in the root directory.
Beta
I strongly suspect that's not what Appu really wants, as a Makefile that works only when *not* run from its own directory is borderline insane. This is the proper technique for running makefiles from "somewhere else".
Andy Ross
1) Nobody (before you) has suggested a Makefile that works only when *not* run from its own directory. 2) This is not the way to run Makefiles from somewhere else-- TRY IT.
Beta
+4  A: 

The M-x compile function looks up the compile-command-variable which you can override on the promt -- so just replace it with something like

(cd ../.. && make && cd -)

and let that run.

I also often use a file header (in line 1) such as

// -*- compile-command: "g++ -o myprog myprog.ccc -lfoo -lbar && ./myprog"; -*-

which you can generalize at will with different options. After reloading the file, M-x compile will execute your custom compile command which I find quite useful.

Dirk Eddelbuettel
Appu
Actually, I prefer compile-command in each source file. That way they get set when loaded -- perfect in a directory like ~/cpp/misc/ where I may need Boost for test files, Qt for others etc pp. But if you always need the same compile-command the ~/.emacs is valid as well.
Dirk Eddelbuettel
+2  A: 

I use EDE (from CEDET) to define projects, and store compilation commands in the project definition. Look to my config for examples: lines 105-133 -- examples of projects, lines 135-165 -- code, that defines compilation functions, and lines 168-189 -- functions for different kinds of projects -- standard (compile from root directory), and cmake (compilation in separate directory)

Alex Ott
Thanks. Unfortunately, EDE is not working at my end. I can't see the project menu. I have already followed your article but it didn't helped for EDE.
Appu
A: 

This should do it:

make -f path_to_rootdir/Makefile -I path_to_rootdir/tests

The -f tells it what makefile to use, the -I tells it where to look for files to be included in the makefile (that aren't in the local directory).

Beta
This may work, but any tools run by make are going to have their working directory set somewhere other than they expect. The proper technique is the -C option, as opello details below. Or putting a cd command into the M-x compile command itself, as Dirk explains above.
Andy Ross
You are wrong, Mr. Ross, on all three points.
Beta
Argument without evidence. Just check the man page if you don't believe me; the -f argument specifies the makefile, it doesn't change the working directory. The -I option is a hack to get around that problem for **make itself**, but not any of the processes it spawns. If they have any dependencies on cwd (most do), they aren't going to work right.
Andy Ross
Yes, -f specifies the makefile, *as I said*. Yes, it doesn't change the working directory, *which is what we're trying for*. Using -C or cd will cause the build to *fail, as Appu explained*. Spawned processes will act in the local directory, *as they should*. Shall I continue?
Beta
A: 

Start with M-x compile RET. When it prompts for a command, just enter cd /path/to/root && make and hit return. This command works for all variants of make and handles the "included makefile" problem without any extra flags.

The next time you type M-x compile RET, this new string will be presented as the default, so you only have to hit return. On the off chance that you're actively compiling multiple projects within emacs, you can use M-p and M-n to move backwards and forwards through the history of compile comamnds.

Dale Hagglund
Thanks. I have added that to emacs initialization file and everything works now.
Appu
My point was that you needn't do any .emacs customizations. However, there are multiple solutions to every emacs problem so if you've found something that works, that's good enough.
Dale Hagglund
+1  A: 

After a while of different attempts to make EDE work the way I wanted, I went for .dir-locals.el:

((c++-mode . ((compile-command . "make -C ../build -j2 whatever"))))

I found it slightly better for me than having a // -*- -*- in a header of every file, and a whole lot better than specifying in my init.el (or any other config) those ede-cpp-root-project with full paths to projects, which I either create too often or move all of a sudden :)

Nice addition to the scheme was cmake which makes compile errors to be 'properly jumpable' since it uses full paths in generated makefiles.

Dmitry
A: 

(I use scons, but the principle is the same. Change SConstruct to Makefile and scons to make...)

I've customized by .emacs so that it always compiles the project containing the current buffer's file, however deeply nested; it searches upwards for the first SConstruct and uses that as it's project root directory.

Here's a couple of functions which search up the directory hierarchy looking for SConstruct.

;; inspired by jds-find-tags-file in http://www.emacswiki.org/emacs/EmacsTags
(defun find-sconstruct ()
"recursively searches upwards from buffer's current dir for file named SConstruct and returns that dir. Or nil if not found or if buffer is not visiting a file"
  (labels
      ((find-sconstruct-r (path)
           (let* ((parent (file-name-directory path))    
            (possible-file (concat parent "SConstruct")))
          (cond
           ((file-exists-p possible-file)
            (throw 'found-it possible-file))
           ((string= "/SConstruct" possible-file)
            (error "No SConstruct found"))
           (t (find-sconstruct-r (directory-file-name parent)))))))
    (if (buffer-file-name)
     (catch 'found-it
       (find-sconstruct-r (buffer-file-name)))
      (error "Buffer is not visiting a file"))))


(defun project-root ()    
  (file-name-directory (find-sconstruct)))

You can then change your compile-command to use project-root e.g.

(concat "cd " (project-root) " && scons")
James G
+1  A: 

Another alternative is to set the variable compilation-process-setup-function which is documented as:

*Function to call to customize the compilation process. This function is called immediately before the compilation process is started. It can be used to set any variables or functions that are used while processing the output of the compilation process. The function is called with variables compilation-buffer' and compilation-window' bound to the compilation buffer and window, respectively.

I use Maven alot and wrote this library to support your issue for a Maven context. In the following, change the value of the variable compile-search-file as appropriate:

;;; Support for Maven 2

(require 'compile)

(setq compile-search-file "pom.xml")

(defun find-search-file ()
  ;; Search for the pom file traversing up the directory tree.
  (setq dir (expand-file-name default-directory))
  (let ((parent (file-name-directory (directory-file-name dir))))
    (while (and (not (file-readable-p (concat dir compile-search-file)))
     (not (string= parent dir)))
      (setq dir parent
        parent (file-name-directory (directory-file-name dir))))
    (if (string= dir parent)
    (error "Search file %s is missing" compile-search-file)
      (with-current-buffer compilation-last-buffer
    (message "Test %s %s." compilation-buffer compilation-window)
    (setq default-directory dir)))))

;; Add the following to support Emacs' compile mode:
(add-to-list
 'compilation-error-regexp-alist-alist
 '(mvn "^\\(.*\\):\\[\\([0-9]*\\),\\([0-9]*\\)\\]" 1 2 3))
(add-to-list 'compilation-error-regexp-alist 'mvn)

(setq compilation-process-setup-function 'find-search-file)

(provide 'maven-support)
pajato0
That's great, thanks!
Sébastien RoccaSerra