Set vimrc makeprg to custom script if no Makefile - vim

I want the :make command to execute normally if there is a Makefile present. Otherwise I want it to execute a custom script, say, called compile. If neither file is present, I'd like it to fail gracefully (ideally not leave the editing screen).
I'm aware of this thread, which presents a bash expression I was able to adapt to suit my needs. EXCEPT I sometimes need to run a shell other than bash. Is there a shell-independent way to do this?

makeprg can be set to the name of any executable. Make a shell script that runs make if there is a Makefile, otherwise run compile, or else return.

Related

Vim makeprg make environment variable expansion

This is bothering me more than it should and has me completely stumped. I feel like like finding the answer will have some good learning opportunities so hopefully it's relevant.
I do embedded C development with Vim and have a setup for hobbyist stuff with Arduino (using Arduino Makefile). I use :make with some shortcuts with build projects.
An external define resolves the Arduino Makefile root directory in the project level Makefile: 'ARDMK_DIR=/usr/local/opt/arduino-mk'. This is define as an export in my shell (zsh). This is where it gets weird:
Using make at the shell prompt the project builds fine:
make -d
This program built for i386-apple-darwin11.3.0
Reading makefiles...
Reading makefile `Makefile'...
Reading makefile `/usr/local/opt/arduino-mk/Arduino.mk' (search path) (no ~ expansion)...
However using :make in Vim the define becomes something from an old install:
:make
This program built for i386-apple-darwin11.3.0
Reading makefiles...
Reading makefile `Makefile'...
Reading makefile `/usr/local/Cellar/arduino-mk/1.5.2/Arduino.mk' (search path) (no ~ expansion)...
Makefile:24: /usr/local/Cellar/arduino-mk/1.5.2/Arduino.mk: No such file or directory
I cannot for the life of me find where ARDMK_DIR is being re-defined to '/usr/local/Cellar/arduino-mk/1.5.2'. Things I have tried:
setlocal makeprg=echo\ $ARDMK_DIR\ &&\ make\ -d\: echo comes back with my shell define (/usr/local/opt/arduino-mk), but make fails with the error above!!
:echo $ARDMK_DIR: again returns my shell define.
ag my home directory for ARDMK_DIR, the only place it is defined is in my shell exports. Did since for my root directory to and same thing. Same thing for $VIMRUNTIME
Even vim-disptach works fine calling the same makeprg?!
Re-define ARDMK_DIR in the project Makefile. Everything builds find as expected. I don't want to do this however as I compile with different systems.
The same vim config works on other macOS and Linux systems with expected behaviour.
Some where between echo and the actual execution of make, ARDMK_DIR is being re-defined. Why and can anyone think of a way of finding out where and solving this?
Zsh has multiple init files that are sourced. The file .zshenv is always sourced, when the shell starts and the file .zshrc is only sourced when the shell is started in interactive mode.
If you define the variable ARDMK_DIR with different values in .zshenv and in .zshrc, the value from .zshrc will be used when you work interactive with the shell (entering commands, starting Vim, ...).
But when Vim starts a command it will start a non-interactive shell. In that case only the file .zshenv will be sourced, so you get the value from that file.
One question left:
Why did the following command first echo the correct value, but make uses the wrong?
:setlocal makeprg=echo\ $ARDMK_DIR\ &&\ make\ -d\
For testing, I started Vim under strace. Then :
:set makeprg=echo\ $EDITOR
:make
In the strace file I found the following line:
execve("/usr/bin/zsh", ["/usr/bin/zsh", "-c", "echo vi 2>&1| tee /tmp/vdxR5DH/"...], [/* 86 vars */]) = 0
As you can see, Vim executes echo vi, so it already expanded the environment variable $EDITOR to its value before calling the shell.
So the answer to the above question is, that the echo command echos the text, that Vim inserted into the command line while the make command gets the the variable value from the environment. As it is a non-interactive shell, it is the value from .zshenv.

Linux basics - automating a script execution

When beginning to work, I have to run several commands:
source work/tools
cd work/tool
source tool
setup_tool
Off course, doing this a few times a day is really annonying, so I tried to make a bash script tool where I put these commands and put it in /user/bin to run it with command
tool
However, there is a problem. When i run the script and then try to work by typing some of the tool-based commands, it does not work.
I figured out, that it is fine, since if I make a script and then run it, the script seems to run in the same terminal window, but what it really does is, that it behaves as if it created a "hidden window" for its execution and after termination of the script, the "hidden window" terminates too. So I am asking - is there a way to automatize the source command?
I have tried using xterm -hold -e command, but it runs the programmed script in the new window. Obviously, I don't want that. How can I achieve running it in the current window?
Don't put files like that in /usr/bin. As a general rule you don't want to mess with the distribution owned locations like that. You can use /usr/local/bin if you need a system-wide location or you can create a directory in your home directory to hold things like this that are for your own usage (and add that to the $PATH).
What you've noticed is that when run as a script on its own (tool, /path/to/tool, etc.) that the script runs in its own shell session (nothing to do with terminal windows as-such) and you don't want that (as the changes the script makes don't persist to your current shell session).
What you want to do instead is "source"/run the script in your current session. Which you are already doing with that set of commands you listed (source work/tools is doing exactly that).
So instead of running tool or /path/to/tool instead use source /path/to/tool or . /path/to/tool.
As fedorqui correctly points out you don't even need a script for this anywhere as you can just make a shell function for this instead (in your normal shell startup files .bashrc, etc.) and then just run that function when you need to so that setup.
Be careful to use full paths for things when you do this though since you, presumably, want this to work no matter what directory you happen to be in when you run it.
It doesn't create a new hidden window, nor does it create a terminal. What happens is that if you're running a script, normally it runs on a new shell process. The script you're running is supposed to modify the shell environment, but if you're running the script in a new shell process, that shell process's environment is the one that gets modified, instead of your shell environment.
Scripts that needs to modify the current shell environments usually must be run with the source command. What you need to do is to run the script in the current shell. So you should do source /path/to/tool.
If you want to be able to source the script with just tool, put this in your alias file/shell startup (check your distro doc where the file is, but it's usually either .bash_aliases or .bashrc):
alias tool="source /path/to/tool"

VIM. Sending commands to the terminal?

I'm trying to configure vim as my primary coding program. I have figured how to compile single files, but when I go to execute the program from within vim, I keep getting a 127 error code. I have a aliased on my box to ./a.out, however when I issue the command :!a from vim, it doesn't work. :!./a.out does. Does anyone know why this is?
Aliases are defined in rc files that are sourced by interactive shell only and work only in interactive mode (vim does pass everything to shell, it never executes anything except the shell directly with fork+execve).
By default shell launched from vim starts in non-interactive mode hence bashrc is not read and no aliases are defined (though even if they were defined, they won’t be used in non-interactive mode). You may set
set shellcmdflag=-ic
, then shell will be launched in interactive mode and .bashrc file with your alias will be read.
Aliases are a feature of your shell (say, bash). ! operator directly executes a file - shell never sees it, and can't do alias expansion on it. (EDIT: See ZyX)
If you want to make executing your ./a.out easy, you can do something like:
command XX !./a.out
then you can do :XX. Or,
nnoremap X :!./a.out<CR>
then a single key X will suffice.
Another option (which is frowned upon for security reasons) is to add the current directory to your PATH:
PATH="./$PATH"
which will allow you to run your program with just a.out as opposed to ./a.out. If you then compile your program with (a language-dependent isomorphism of) -o switch, you can have it named just a:
gcc -o a foo.c
Given this, !a would work. Use at your own risk.
EDIT: ZyX is correct-er. :) I'll leave the answer here for the other information.

Can I run current script or a script in ViM?

I am developing a php application using ViM. Is there a shortcut for me to run the currentEditing.php? Alternatively, is there a shortcut for running main.php?
It depends on your platform, certainly, but when I'm developing python I often run the current script just by executing :!%. The colon for a command (obviously), the bang for shell execute, and the percent for the current filename.
You can execute shell commands in ViM using the following syntax:
:!command -options arguments
Therefore, you need to save your file first, and then run whatever command you need for executing php.
I don't know php, so let me give you an example with compiling a C file:
... editing text
:w main.c # save to file
:!gcc -Wall main.c # compile the code
:!./a.out # execute the executable
Note that :! commands are run by shell and ViM has no understanding of it. Therefore, you can execute any command. This also means that the command cannot run on a modified, unsaved buffer.

Scripting on Linux

I am trying to create a script that will run a program on each file in a list. I have been trying to do this using a .csh file (I have no clue if this is the best way), and I started with something as simple as hello world
echo "hello world"
The problem is that I cannot execute this script, or verify that it works correctly. (I was trying to do ./testscript.csh which is obviously wrong). I haven't been able to find anything that really explains how to run C Scripts, and I'm guessing there's a better way to do this too. What do I need to change to get this to work?
You need to mark it as executable; Unix doesn't execute things arbitrarily based on extension.
chmod +x testscript.csh
Also, I strongly recommend using sh or bash instead of csh, or you will soon learn about the idiosyncrasies of csh's looping and control flow constructs (some things only work inside them if done a particular way, in particular with the single-line versions things are very limited).
You can use ./testscript.csh. You will however need to make it executable first:
chmod u+x testscript.csh
Which means set testscript to have execute permissions for the user (who ever the file is owned by - which in this case should be yourself!)
Also to tell the OS that this is a csh script you will need put
#! /path/to/csh
on the first line (where /path/to/csh is the full path to csh on your system. You can find that out by issuing the command which csh).
That should give you the behvaiour you want.
EDIT As discussed in some of the comments, you may want to choose an alternative shell to C Shell (csh). It is not the friendliest one for scripting.
You have several options.
You can run the script from within your current shell. If you're running csh or tcsh, the syntax is source testscript.csh. If you're running sh, bash, ksh, etc., the syntax is . ./testscript.sh. Note that I've changed the file name suffix; source or . runs the commands in the named file in your current shell. If you have any shell-specific syntax, this won't work unless your interactive shell matches the one used by the script. If the script is very simple (just a sequence of simple commands), that might not matter.
You can make the script an executable program. (I'm going to repeat some of what others have already written.) Add a "shebang" as the first line. For a csh script, use #!/bin/csh -f. The -f avoids running commands in your own personal startup scripts (.cshrc et al), which saves time and makes it more likely that others will be able to use it. Or, for a sh script (recommended), used #!/bin/sh (no -f, it has a completely different meaning). In either case, run chmod +x the_script, then ./the_script.
There's a trick I often use when I want to perform some moderately complex action. Say I want to delete some, but not all, files in the current directory, but the criterion can't be expressed conveniently in a single command. I might run ls > tmp.sh, then edit tmp.h with my favorite editor (mine happens to be vim). Then I go through the list of files and delete all the ones that I want to leave alone. Once I've done that, I can replace each file name with a command to remove it; in vim, :%s/.*/rm -f &/. I add a #!/bin/sh at the top save it, chmod +x foo.sh, then ./foo.sh. (If some of the file names might have special characters, I can use :%s/.*/rm -f '&'/.)

Resources