abo-abo
abo-abo

Reputation: 20342

Emacs fastest C++ compilation process?

Here's the use case: I read an articles on tech blogs about C++ (fails of multiple inheritance this and multi-threading that etc.:). Usually they come with some code. It's almost always one file and I almost always want to run it and play around with it.

I want to do it with Emacs and I want to do it FAST, as in with the least (or reasonably few) keystrokes.

So suppose I've already created a multiple_inheritance.cc file in its own folder and pasted the code. How do I get to an executable really fast?

Here's what I'm doing now (hopefully someone will improve it.)

(defun cpp-generate-makefile ()
  (interactive)
  (let* ((n-buffer (buffer-file-name))
         (n-file (file-name-nondirectory n-buffer))
         (n-target (file-name-sans-extension n-file))
         (n-makefile (concat (file-name-directory n-buffer) "Makefile")))
    (if (file-exists-p n-makefile)
        (when (called-interactively-p 'any)
          (message "Makefile already exists"))
      (with-current-buffer (find-file-noselect n-makefile)
        (insert 
         (concat n-target ": " n-file 
                 "\n\tg++ -g -O2 -std=c++0x -o $@ $^\n\n"
                 "clean: \n\trm -f " n-target "\n"))
        (save-buffer)))))

(defun cpp-run ()
  (interactive)
  (save-buffer)
  (cpp-generate-makefile)
  (compile "make"))

(add-hook 'c++-mode-hook
      (lambda()
            ;; ...
            (define-key c++-mode-map [f5] 'cpp-run)))

Here's a few things that SLOW me down currently:

  1. compile asks if I want to save any open files that are totally unrelated to C++.
  2. I'd like to have the point in *compilation* buffer in case there was an error. I've looked into compile.el, which has compilation-num-errors-found defined, but that variable isn't used anywhere in that file.
  3. On the other hand, if there were no errors (I just need a predicate for this), why not launch term and run the program?

Upvotes: 4

Views: 1276

Answers (2)

abo-abo
abo-abo

Reputation: 20342

I've updated the code I had to add a run target to Makefile. I've also added an extension to C:

(defvar cpp-generate-compiler "g++ -g -O2 -std=c++0x")

(defun cpp-generate-makefile ()
  (interactive)
  (let* ((n-buffer (buffer-file-name))
         (n-file (file-name-nondirectory n-buffer))
         (n-target (file-name-sans-extension n-file))
         (n-makefile (concat
                       (file-name-directory n-buffer)
                       "Makefile")))
    (if (file-exists-p n-makefile)
        (when (called-interactively-p 'any)
          (message "Makefile already exists"))
      (with-current-buffer (find-file-noselect n-makefile)
        (insert
         (concat n-target ": " n-file
                 (format "\n\t%s -o $@ $^"
                   cpp-generate-compiler)
                 "\n\nclean: \n\trm -f " n-target
                 "\n\nrun: " n-target "\n\t ./" n-target
                 "\n\n.PHONY: clean run\n"))
        (save-buffer)))))

(defun cpp-run ()
  (interactive)
  (save-buffer)
  (cpp-generate-makefile)
  (compile "make run"))

(defun c-run ()
  (interactive)
  (let ((cpp-generate-compiler "gcc -g -O2 -std=c99"))
    (cpp-run)))

(add-hook 'c++-mode-hook
      (lambda()
            ;; ...
            (define-key c++-mode-map [f5] 'cpp-run)))
(add-hook 'c-mode-hook
      (lambda()
            ;; ...
            (define-key c-mode-map [f5] 'c-run)))
            
(setq compilation-ask-about-save nil)
(setq compilation-finish-functions
      (list (lambda(buffer str)
              (unless (string= str "finished\n")
                (push-mark)
                (next-error)))))

With (setq compilation-ask-about-save nil) there's no more warnings about saving (cpp-run saves automatically).

And I just have to remember that M-g n and M-g p navigate the errors.

Now my process is neary optimal: one key from source to result in case there are no errors.

In case there are errors, it's an extra M-g n. Now, if only there was a way for compile to call (push-mark)(next-error)...

UPD:

Thanks to suggestion of @juanleon, this is solved with:

(setq compilation-finish-functions
      (list (lambda(buffer str)
              (unless (string= str "finished\n")
                (push-mark)
                (next-error)))))

But for some reason, push-mark doesn't work properly in this case.

Upvotes: 0

Malabarba
Malabarba

Reputation: 4553

To sum up the answers in the comments.

  1. Change the value of compilation-ask-about-save. If you don't want to change this globally (with setq) you can change it just inside your function by wrapping it in a (let ((compilation-ask-about-save nil)) ...function contents...) statement.
  2. Bind next-error to something quick: (define-key c++-mode-map "\M-j" 'next-error). I personally use global-set-key for this, just because it's useful in a whole bunch of modes.
  3. Replace the compile command in your function with (compile "make && ./a.out") (using the name of your executable of course).

Hope this helps.

Upvotes: 2

Related Questions