Self-compiling .cpp file

Alec Jacobson

November 17, 2011

weblog/

I often put compilation notes in the comments at the top of small .cpp programs. These are just reminders to me of the flags I need to set in g++ to compile the program. For bigger programs I use Xcode or Makefiles, but often if I'm just testing a small idea I don't want that overhead. I had a funny idea today that my .cpp file could act as both a bash script and as C++ code. Then I could execute the .cpp file (as a bash script) which would set up the compiler and then compile itself. Here's a proof of concept, save this in main.cpp

#!/bin/bash
//usr/bin/tail -n +2 $0 | g++ -o main -x c++ - && exit
#include <cstdio>
int main(int argc,char * argv[])
{
  printf("Hello, world\n");
  return 0;
}

Make it executable with chmod +x main.cpp

To compile just execute the cpp file:

./main.cpp

This creates the real binary of the program which you can execute with:

./main

Update: If you don't like the hassle of making the binary you can just run the binary right after you compile it, then delete it immediately. Do this by changing the second line to:

#!/bin/bash
//usr/bin/tail -n +2 $0 | g++ -o main -x c++ - && ./main && rm main && exit
#include <cstdio>
int main(int argc,char * argv[])
{
  printf("Hello, world\n");
  return 0;
}

Update: With an additional hack you can have a full fledged bash script at the top of your .cpp file:

#!/bin/bash
/*/../bin/ls > /dev/null
# BEGIN BASH SCRIPT
printf "//" | cat - $0 | g++ -o .main -x c++ - && ./.main $@
rm -f .main
# END BASH SCRIPT
exit
*/
#include <cstdio>
int main(int argc,char * argv[])
{
  printf("Hello, world\n");
  return 0;
}

Update: Here's another one I found especially useful. This allows me to pipe the output to colormake to get colorized terminal compiler output.

#!/bin/bash
/*/../bin/ls > /dev/null
# BEGIN BASH SCRIPT 
export PS4=""
set -o xtrace       
printf "//" | cat - $0 | g++ -o .main -x c++ - && ./.main $@
rm -f .main         
# END BASH SCRIPT
exit
*/
#include <cstdio>
int main(int argc,char * argv[])
{
  printf("Hello, world\n");
  return 0;
}

Then I pipe the output to colormake using:

./main.cpp 2>&1 | colormake.pl

Update: Yet another one. This time works with the debugger.

TEMP="$0.cpp" printf "//" | cat - $0 >$TEMP

#!/bin/bash
/*/../bin/ls > /dev/null
# BEGIN BASH SCRIPT 
source ~/.profile
export PS4=""
set -o xtrace       
printf "//" | cat - $0 | 
g++ -g -O0 -std=c++11 -o .main $TEMP && \ 
/Applications/Xcode.app/Contents/Developer/usr/bin/lldb -b -o r ./main -- "$@"
rm -f .main         
# END BASH SCRIPT
exit
*/
#include <cstdio>
int main(int argc,char * argv[])
{
  printf("Hello, world\n");
  return 0;
}

Update: I edited the above to include source ~/.profile. Seems with El Capitan Mac OS 10.11 the DYLD_FALLBACK_LIBRARY_PATH setting is not persistent when call a script from vim.

Comments

November 17, 2011, Colin
This is a great idea, esp. with the auto-deleting... probably the closest C++ will be to a scripting language.