Make and build automation

Trying to understand makefiles caused me quite the trouble back when I was working on my game engine a few years back. I eventually settled on a lukewarm solution that worked, but always recompiled the entire game it was targeting. No matter how I looked at it I wasn't happy with the makefile format and ended up writing my own build script that would read flags and libraries from a yaml file. It worked surprisingly well and only compiled source files that needed to be (re)compiled.
This weekend I took another look at make. Learned a few additional things. In the end I still wasn't happy. However I also found myself no longer quite so satisfied with the build script I had written. It's too inflexible and the yaml tree it operates on is actually quite ugly to write and read. So I decided I'd try to invent my own flavor of Make. Here's the planned result:

001  GLOB=Successfully built target $@ for $!: $(BIN). 
002  @geneiseidenki
003      SRC=src #because src files are shared between platforms
004      SRC_EXT=c # or cpp if c++ project
005      # shared compiler and linker flags
006      CFLAGS=-Iinclude -Imruby-2.1.0/include
007      LIBS=-lm
008  
009      debug: # label, runs if given as arg
010          CFLAGS+= -D_STN_DEBUG 
011          echo "running debug for @$@"
012  
013      _plat: # executes platforms
014  
015      verbose:
016          echo "finished building @$@"
017  
018  linux: @geneiseidenki
019      CC=gcc
020      BIN=bin/$@-$! # $! replaced by platform
021      OBJ=obj/linux
022      CFLAGS+= `sdl2-config --cflags` 
023      LIBS+= `sdl2-config --libs` -lSDL2_image \
024             -lSDL2_mixer mruby-2.1.0/build/host/lib/libmruby.a
025  
026      debug:
027          OBJ=obj/linux/debug
028          BIN=bin/$@-$!-debug
029          CFLAGS+= -g -Wall -Wno-switch -Wno-unused-label
030  
031      _pre:
032  
033      _rule: # runs for each source file with given extension
034          # each time returns a dependency list for the given src file
035          # which is used to determine if it requires to be recompiled
036          $(CC) -M $~ $(CFLAGS) # $~ replaced with given src file
037  
038      _compile: # runs for each source files that needs to be recompiled
039          $(CC) -c $~ -o $& $(CFLAGS) # $& replaces .$(SRC_EXT) with .o in $~
040  
041      _link: # links all object files together
042          $(CC) -o $(BIN) $&& $(LIBS) # $&& expands to list of all object files
043  
044      _post:
045          chmod +x $(BIN)
046          echo $(GLOB)
047  
048  win64: @geneiseidenki
049      CC=x86_64-w64-mingw32-gcc
050      OBJ=obj/win64
051      BIN=bin/$@-$!.exe
052      CFLAGS+= -Ilib/SDL2-2.0.9/x86_64-w64-mingw32/include/SDL2 \
053               -Ilib/SDL2_image-2.0.4/x86_64-w64-mingw32/include/SDL2 \
054               -Ilib/SDL2_mixer-2.0.4/x86_64-w64-mingw32/include/SDL2
055      LIBS+= -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer \
056             -Llib/SDL2-2.0.9/x86_64-w64-mingw32/lib \
057             -Llib/SDL2_image-2.0.4/x86_64-w64-mingw32/lib \
058             -Llib/SDL2_mixer-2.0.4/x86_64-w64-mingw32/lib \
059             mruby-2.1.0/build/win64/lib/libmruby.a -lws2_32
060  
061      debug:
062          OBJ=obj/win64/debug
063          BIN=bin/$@-$!-debug.exe
064  
065      _pre:
066  
067      _rule:
068          $(CC) -M $~ $(CFLAGS) -D_WIN32
069  
070      _compile:
071          $(CC) -c $~ -o $& $(CFLAGS) -D_WIN32
072  
073      _link:
074          $(CC) -o $(BIN) $&& $(LIBS)
075  
076      _post:
077          echo $(GLOB)
078  
079  # usage exampled
080  
081  # compiles geneiseidenki for linux
082  # rmk @geneiseidenki linux
083  
084  # builds debug executables for all targets for the linux platform
085  # rmk linux debug
086  
087  # runs clean labels for all targets, all platforms
088  # in verbose mode, without runnig any of the compilation steps 
089  #(l = label-only)
090  # rmk clean -vl
091  

Here @geneiseidenki is the target, the program to build. linux and win64 are platforms. debug is a label. The idea is that each target has one or more platforms. The build automation tool will attempt to make each target application for each platform, only (re)compiling and linking outdated source and object files. It doesn't do anything new and is less flexible than classic make, but maps closer to how I felt make would actually work before I fully understood it. Labels can modify the build process at about any stage since they are executed in order.
Now all that's left is implementing the program that parses and uses my custom format.

For comparison here is the original Makefile:

001  include _config.mk
002  include config.mk
003  MSG = make 
004  
005  # linux
006  ifeq ($(filter linux,$(MAKECMDGOALS)),linux)
007  LIB = `sdl2-config --cflags --libs` -lSDL2_image -lSDL2_mixer \
008        mruby-2.1.0/build/host/lib/libmruby.a -lm
009  INC = -Imruby-2.1.0/include
010  CC = gcc
011  RUN = -o bin/$(NAME)
012  MSG += linux
013  endif
014  
015  # windows 64
016  ifeq ($(filter win64,$(MAKECMDGOALS)),win64)
017  LIB = -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer \
018        -Llib/SDL2-2.0.9/x86_64-w64-mingw32/lib \
019        -Llib/SDL2_image-2.0.4/x86_64-w64-mingw32/lib \
020        -Llib/SDL2_mixer-2.0.4/x86_64-w64-mingw32/lib \
021        mruby-2.1.0/build/win64/lib/libmruby.a -lm
022  INC = -Ilib/SDL2-2.0.9/x86_64-w64-mingw32/include/SDL2 \
023        -Ilib/SDL2_image-2.0.4/x86_64-w64-mingw32/include/SDL2 \
024        -Ilib/SDL2_mixer-2.0.4/x86_64-w64-mingw32/include/SDL2 \
025        -Imruby-2.1.0/include -lws2_32
026  CC = x86_64-w64-mingw32-gcc
027  RUN = -o bin/$(NAME).exe
028  SFLAG = -D_WIN32
029  MSG += win64
030  endif
031  
032  # windows 32
033  ifeq ($(filter win32,$(MAKECMDGOALS)),win32)
034  LIB =
035  INC =
036  CC = i686-w64-mingw32-gcc
037  RUN = -o bin/$(NAME).exe
038  SFLAG = -D_WIN32
039  MSG += win32
040  endif
041  
042  # platform independent
043  
044  INC += -Iinclude
045  ifeq ($(filter dev,$(MAKECMDGOALS)),dev)
046  CFLAG = -g -Wall -Wno-switch -Wno-unused-label
047  SFLAG += -D_DEBUG
048  MSG += dev
049  endif
050  
051  ifeq ($(filter macro,$(MAKECMDGOALS)),macro)
052      #CFLAG += -E
053      RUN = -E bin/$(NAME)
054  endif
055  
056  #linux: $(OBJ) // does not recompile when header files change
057  linux win64: $(SATENOBJ) $(OBJ)
058      @echo $(MSG)
059      $(CC) $(CFLAG) $(RUN) $(SFLAG) $(SATENOBJ) $(OBJ)  $(LIB) $(INC)
060  
061  dev:
062      @echo Compiled in debug mode.
063  
064  macro:
065      @echo Wrote preprocessor output to stdout
066  
067  
068  
069  
070  #release:
071  #   gcc -Wall -o game $(SRC) `sdl2-config --cflags --libs` $(ADD_LIBS)
072  #
073  #gcc -M src/main.c `sdl2-config --cflags --libs` -lSDL2_image -lSDL2_mixer -Imruby-2.1.0/include -Iinclude mruby-2.1.0/build/host/lib/libmruby.a -lm
074