I often want to try things out in c++ without going to the trouble of writing a Makefile, creating a project or typing in complex command lines.
I wondered whether it's possible to make a .cpp file which is also a bash script, so it can compile and run itself.
I also wanted to be able to specify command line options in the script, in case there were dependencies like boost and so on.
Rather than making a script that is also a cpp file that compiles itself, why not just make a script that watches the cpp file (or dir it is in) and re-compiles the cpp file on any new changes? inotifywait
was made for that. While this doesn't precisely meet your question requirements, it will keep your code from carrying around all the script baggage. Example:
.cpp file
#include <iostream>
using namespace std;
int main (void) {
cout << endl << "Code Running (ver. 10)" << endl << "and being timed." << endl;
return 0;
}
inotifywait Script
#!/bin/bash
# watchdir=${1:-$PWD}
src=${1:-app.cpp} ## set the source and output (exe) names
out=${src%.cpp}
while inotifywait -q -e modify -e modify -e close_write -e delete "$PWD"; do
printf "\n compiling 'g++ %s -o %s'\n\n" $src $out
[ -f $out ] && rm $out ## remove existing exe before building
g++ -o $out $src ## build new exe
[ -f $out ] || { ## validate new exe created
printf "error: compilation failed. exiting script.\n\n"
printf "usage: %s source.cpp (default: app.cpp)\n\n"
exit 1
}
[ -x $out ] || { ## validate it is executable
printf "error: file produced from compilation is not executable.\n"
exit 1
}
time ./$out 2>&1 ## compute elapsed time
exesz=$(du -h $out | awk '{print $1}') ## store disk usage (removing name)
## print results
printf "\n Source file: %s\n" "$src"
printf " output file: %s\n" "$out"
printf " size of exe: %s bytes\n\n" "$exesz"
done
You can either run the script in the foreground or background. e.g.:
Example (output on app.cpp save)
$ ./watch.sh
/home/david/scr/tmp/stack/dat/tmp/bld/ MODIFY app.cpp
compiling 'g++ app.cpp -o app'
Code Running (ver. 10)
and being timed.
real 0m0.003s
user 0m0.001s
sys 0m0.001s
Source file: app.cpp
output file: app
size of exe: 16K bytes
... it turns out that it is possible:
/*/../bin/ls > /dev/null
filename=$(basename $BASH_SOURCE)
dirname=$(cd $(dirname $BASH_SOURCE) && pwd)
if [ "${dirname}" == "$(pwd)" ]
then
mkdir -p ${dirname}/bin > /dev/null
filename="${dirname}/bin/${filename}"
else
filename="./${filename}"
fi
filename=${filename%.*}
if [ $0 -nt ${filename} ]
then
c++ -o "${filename}" -std=c++1y $BASH_SOURCE || exit
fi
("${filename}" && exit) || echo $? && exit
exit
*/
#include <iostream>
using namespace std;
auto main() -> int
{
cout << "Hello, World" << endl;
return 0;
}
In this case I have named the file skeleton.cpp
:
$ chmod +x skeleton.cpp
$ ./skeleton.cpp
Hello, World
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With