Configure Emacs Flymake to call g++ directly - emacs

When writing simple, one file, C++ code, I usually call g++ directly. By default, Flymake seems to assume the presence of a Makefile with a check-syntax target. How do I configure Flymake to simply call g++ directly, e.g:
g++ -c a.cpp
If the answer could be modified to include compiler flags, that would be even better
Many thanks

You can call g++ directly with following flymake configuration.
(require 'flymake)
(defun flymake-cc-init ()
(let* ((temp-file (flymake-init-create-temp-buffer-copy
'flymake-create-temp-inplace))
(local-file (file-relative-name
temp-file
(file-name-directory buffer-file-name))))
(list "g++" (list "-Wall" "-Wextra" "-fsyntax-only" local-file))))
(push '("\\.cpp$" flymake-cc-init) flymake-allowed-file-name-masks)
(add-hook 'c++-mode-hook 'flymake-mode)
I got following screenshot when I call flymake-allowed-file-name-masks
Following is comment version.
;; Import flymake
(require 'flymake)
;; Define function
(defun flymake-cc-init ()
(let* (;; Create temp file which is copy of current file
(temp-file (flymake-init-create-temp-buffer-copy
'flymake-create-temp-inplace))
;; Get relative path of temp file from current directory
(local-file (file-relative-name
temp-file
(file-name-directory buffer-file-name))))
;; Construct compile command which is defined list.
;; First element is program name, "g++" in this case.
;; Second element is list of options.
;; So this means "g++ -Wall -Wextra -fsyntax-only tempfile-path"
(list "g++" (list "-Wall" "-Wextra" "-fsyntax-only" local-file))))
;; Enable above flymake setting for C++ files(suffix is '.cpp')
(push '("\\.cpp$" flymake-cc-init) flymake-allowed-file-name-masks)
;; Enable flymake-mode for C++ files.
(add-hook 'c++-mode-hook 'flymake-mode)

Related

Spawing ctags from Emacs with start-process

I have a function that is supposed to create a ctags file and load it into Emacs asynchronously. ctags can take a while to run if invoked on really big files, and I don't want my function to do any blocking, thus I use start-process. This is what it all looks like:
(defun temp-tags-file-for-file (file)
"Generate a temporary tags file for FILE.
Add the file to tags list and return the name of the file."
(if (not (boundp 'ctags-command))
(setq ctags-command "/usr/bin/ctags"))
(let* ((temp-file (make-temp-file "EMACS_TAGS"))
(proc (start-process "temp-tags-proc" nil ctags-command
"-f" temp-file file)))
(set-process-sentinel proc
(lambda (proc msg)
(when (eq (process-status proc) 'exit)
(if (boundp 'temp-tags-file)
(progn
(add-to-list 'tags-table-list
temp-tags-file)
(makunbound 'temp-tags-file))))))
(setq temp-tags-file temp-file)
temp-file))
For some reason, the tags file is always blank. Calling ctags with the exact same parameters from the shell generates a non-blank, working tags file. How do I get ctags to print its output properly?
If ctags wants the shell, just give it to the shell:
(start-process "temp-tags-proc" nil shell-file-name shell-command-switch
(format "/usr/bin/ctags ~/Dropbox/source/c/*.c -f %s"
(make-temp-file "EMACS_TAGS")))

for single file non-interactive c++ programs, print the compile output and output of program(runtime) in same buffer

How to compile a single file c++ program, print the output to compile buffer, run the c++ executable if compilation success and append the output of the c++ executable to compile output.
The below elisp code can compile the c++ program with the same name as buffer name. Now how to run and append the output of the exectutable to the compile buffer
(defun cpp-single-file-compile ()
(interactive)
(save-buffer)
(compile
(concat "g++ -g " (buffer-file-name) " -o " (file-name-sans-extension (buffer-file-name)))))
(add-hook 'c++-mode-hook (lambda () (local-set-key "\C-c\C-c" 'cpp-single-file-compile)))
The solution is very simple. Just execute after compilation
(defun cpp-single-file-compile ()
(interactive)
(save-buffer)
(compile
(concat "g++ -g " (buffer-file-name) " -o " (file-name-sans-extension (buffer-file-name))
" && " (file-name-sans-extension (buffer-file-name)) ".exe")))
(add-hook 'c++-mode-hook (lambda () (local-set-key "\C-c\C-c" 'cpp-single-file-compile)))
Additionally, to your code I have a hack-local-variables in the C-c C-c command:
(defun compileCompileCommand ()
"Compile current file."
(interactive)
(hack-local-variables)
(unless file-local-variables-alist
(setq compile-command (concat "g++ -g " (buffer-file-name) " -o " (file-name-sans-extension (buffer-file-name))
" && " (file-name-sans-extension (buffer-file-name)) ".exe")))
(compile compile-command))
(add-hook 'c-mode-common-hook '(lambda ()
(local-set-key (kbd "C-c C-c") 'compileCompileCommand)))
Then I add local variables to my source files. Example:
#include <iostream>
int main() {
std::cout << "Test";
return 0;
}
/*
Local Variables:
compile-command: "g++ -g test.cc && ./a.exe"
End:
*/
Therewith, you can decide on a per-file basis what you want to do. Changes of the compile-command are regarded at the next compilation because of the hack-local-variables thing.
The output is:
-*- mode: compilation; default-directory: "/c/temp/" -*-
Compilation started at Fri Nov 15 15:22:34
g++ -g test.cc && ./a.exe
Test
Compilation finished at Fri Nov 15 15:22:34
EDIT: Sometimes it is necessary to link some additional library or specify some additional include path. Therefore, I could not accept the answer of Talespin_Kit as a complete solution. I think a combination as it is given in the above edited version is better. If local variables are given then the original compile-command is used else an automatically generated compile-command is used. Note, one could also use assq to check whether compile-command is a member of the local variables.

Use rebar in emacs?

There is a defun in .emacs to get erlang project path, how can I execute a shell-command to do the following:
cd *~/erlang-project-folder*
make
I'm using rebar to build my project, and there is a Makefile to do everything.
I can compile by overriding erlang-compile-function, but I'm not familiar with Emacs Lisp, please help.
Here is my .emacs:
(defun erlang-project-dir ()
(let* ((src-path (file-name-directory (buffer-file-name)))
(pos (string-match "/src/" src-path)))
(if pos (substring src-path 0 (+ 1 pos)) src-path)))
;; there is an error: wrong type argument: commandp
(defun my-inferior-erlang-compile ()
(shell-command.
(concat (concat (concat "cd" erlang-project-dir) "; make"))))
(defvar erlang-compile-function 'my-inferior-erlang-compile)
Instead of relying on directory structure, it's better to try to locate the rebar.config file that is in the root of your project. This could be achieved with following code:
(defun my-find-rebar-root ()
(let ((dir (locate-dominating-file default-directory "rebar.config")))
(or dir default-directory)))
and after that you can use this function for compilation:
(defun my-inferior-erlang-compile ()
(interactive)
(let ((default-directory (my-find-rebar-root)))
(compile "make")))
Although, I'm not sure that the make is right command here - maybe it's better to use rebar compile instead?
P.S. I hope, that I'll find some free time, and will finish rebar support in EDE - in this case, it will be the same unified interface for work with projects.

Erlang flymake with nested folders in src cannot find includes folder

Sorry for my poor English.
I'm configuring my emacs with erlang flymake. Source files in src's nested folders report 'can't find include file', but files in src/folder can find the include file.
My emacs settings for erlang:
;; erlang-mode
(setq load-path (cons "/usr/local/Cellar/erlang/R15B02/lib/erlang/lib/tools-2.6.8/emacs" load-path))
(setq erlang-root-dir "/usr/local/Cellar/erlang/R15B02/lib/erlang")
(setq exec-path (cons "/usr/local/Cellar/erlang/R15B02/lib/erlang/bin" exec-path))
(require 'erlang-start)
;; distel
(add-to-list 'load-path "/usr/local/share/distel/elisp/")
(require 'distel)
(distel-setup)
;; erlang-flymake
(require 'erlang-flymake)
(erlang-flymake-only-on-save)
My erlang application folder is like following:
app/src/ (source code)
src/mod
src/lib
app/include/ (hrls)
app/ebin/ (compiled code)
...etc
In erlang-flymake there are 2 variables (erlang-flymake-get-include-dirs-function and erlang-flymake-get-code-path-dirs-function), that specify functions to search include & ebin directories. Right now, they're pointing to the functions erlang-flymake-get-include-dirs and erlang-flymake-get-code-path-dirs that simply return current dir + include and ebin correspondingly. For example, you can use following code to do this:
(defun get-erlang-app-dir ()
(let* ((src-path (file-name-directory (buffer-file-name)))
(pos (string-match "/src/" src-path)))
(if pos
(substring src-path 0 (+ 1 pos))
src-path)))
(setq erlang-flymake-get-code-path-dirs-function
(lambda ()
(concat (get-erlang-app-dir) "ebin")))
(setq erlang-flymake-get-code-include-dirs-function
(lambda ()
(concat (get-erlang-app-dir) "include")))
P.S. Are you using rebar to maintain your project?
If you use erlang-flymake you might want to look at https://github.com/ten0s/syntaxerl. It's a syntax checker for Erlang. It uses erlang-flymake under the hood, but instead of `erlc' it uses a custom syntax checker that can evaluate .erl, .hrl, .config, .rel, .app, .app.src, .escript files. The syntax checker is rebar aware, but also works with standard Erlang/OTP directory structure. Emacs's setup is also there.

Using Emacs, is it possible to pin the compilation command to a specific buffer/directory?

Right now I am using the following to compile, when I'm in for example main.cpp
C-x b Makefile RET M-x compile RET RET
I actually have M-x compile as a keyboard shortcut, but the problem is I would really like not having to go through all that trouble to simply run my Makefile.
I need to visit Makefile to make sure the compile command is executed using the same directory. Is there any way to pin the directory so I can simply go M-x compile RET RET?
Best regards
Use recompile instead. C-u M-x recompile will let you edit the compile command first. Either way the compile will work out of the directory the last compile was done in.
See my answer here
Directory local variables provide an easy way to trigger the compile from a parent directory of any source file in a subdirectory.
I run emacs primarily on windows.
When I have a makefile that is in a parent directory of a C module, I use this as the compile command:
cd .. && nmake <arguments here>
for example:
cd .. && nmake CONFIG=Debug PLATFORM=x64 target
Beyond that, I find that specifying the make command line that I want to run for various modules is sort of a pain. I wanted a way to attach the default compile command to the buffer being edited. So I wrote a little elisp to handle that job. I figured to insert into the header comments of each buffer a line that would stipulate my preferred compile command, like this:
compile: cd .. && nmake CONFIG=Debug PLATFORM=x64 target
And then have a piece of elisp run, before I invoke M-x compile that grabs the line and proposes it as the compile command I would like to run.
This defun pulls a line out of the header comments:
(defun cheeso-c-get-value-from-comments (marker-string line-limit)
"gets a string from the header comments in the current buffer.
This is used to extract the compile command from the comments. It
could be used for other purposes too.
It looks for \"marker-string:\" and returns the string that
follows it, or returns nil if that string is not found.
eg, when marker-string is \"compile\", and the following
string is found at the top of the buffer:
compile: cl.exe /I uthash
...then this command will return the string
\"cl.exe /I uthash\"
It's ok to have whitespace between the marker and the following
colon.
"
(let (start search-limit found)
;; determine what lines to look in
(save-excursion
(save-restriction
(widen)
(cond ((> line-limit 0)
(goto-char (setq start (point-min)))
(forward-line line-limit)
(setq search-limit (point)))
((< line-limit 0)
(goto-char (setq search-limit (point-max)))
(forward-line line-limit)
(setq start (point)))
(t ;0 => no limit (use with care!)
(setq start (point-min))
(setq search-limit (point-max))))))
;; look in those lines
(save-excursion
(save-restriction
(widen)
(let ((re-string
(concat "\\b" marker-string "[ \t]*:[ \t]*\\(.+\\)$")))
(if (and start
(< (goto-char start) search-limit)
(re-search-forward re-string search-limit 'move))
(buffer-substring-no-properties
(match-beginning 1)
(match-end 1))))))))
Ok, now I need something to invoke that before I invoke compile.
(defun cheeso-invoke-compile-interactively ()
"fn to wrap the `compile' function. This simply
checks to see if `compile-command' has been previously set, and
if not, invokes `cheeso-guess-compile-command' to set the value.
Then it invokes the `compile' function, interactively."
(interactive)
(cond
((not (boundp 'cheeso-local-compile-command-has-been-set))
(cheeso-guess-compile-command)
(set (make-local-variable 'cheeso-local-compile-command-has-been-set) t)))
;; local compile command has now been set
(call-interactively 'compile))
Then of course, the defun that guesses the compile command:
(defun cheeso-guess-compile-command ()
"set `compile-command' intelligently depending on the
current buffer, or the contents of the current directory."
(interactive)
(set (make-local-variable 'compile-command)
(cond
(buffer-file-name
(let ((filename (file-name-nondirectory buffer-file-name)))
(cond
;; editing a C-language source file - check for an
;; explicitly-specified command
((string-equal (substring buffer-file-name -2) ".c")
(let ((explicit-compile-command
(cheeso-c-get-value-from-comments "compile" 34)))
(or explicit-compile-command
(concat "nmake " ;; assume a makefile exists
(file-name-sans-extension filename)
".exe"))))
;; editing a makefile - just run nmake
((string-equal (substring buffer-file-name -8) "makefile")
"nmake ")
;; something else - do a typical .exe build
(t
(concat "nmake "
(file-name-sans-extension filename)
".exe")))))
(t
;; punt
"nmake "))))
The final bit is to bind C-x C-e , normally bound to compile, to the wrapper defun:
(global-set-key "\C-x\C-e" 'cheeso-invoke-compile-interactively)
Now, when I do C-x C-e in the buffer, it searches for the compile command, and proposes to me the command that it finds. I can edit the proposed compile command, then press ENTER and run it.