Writing that perfect Makefile

edit: This little project eventually went nowhere as I kept understanding Make better and better. In the end I deemed it good enough for my purposes and decided writing my own replacement wasn't worth the time.

Realizing that I didn't want xmk to miss Make's core feature made me feel a bit silly for trying to reinvent it. Thus I ended up spending a good couple hours trying to come up with a Makefile that could make me feel satisfied. Pretending I was only ever building for one platform without any separate debug buils led me to this:

001  PRJNAME = my_app                                   # name of bin to build
002  SRCD = src                                         # root dir of src files
003  LIBD = lib                                         # root dir of vendor src files
004  SRC := $(shell find $(SRCD) -name '*.c')           # recursively get all src files from src dir
005  SRC += $(shell find $(LIBD) -name '*.c')           # append those from vendor dir
006  OBJ = $(patsubst %.c, xmk.d/obj/linux/%.o, $(SRC)) # get obj filenames for all src files
007  DEP = $(patsubst %.o, %.d, $(OBJ))                 # filenames to save header dependencies to
008  BIN = xmk.d/bin/linux/$(PRJNAME)                   # where to save the binary...
010  CC = gcc
011  CFLAGS = -Isrc/include
012  LDFLAGS =
015  all: linux win64                                   # we omit the win64 build for brevity
018  $(BIN): $(OBJ)                                     # this is run last in the chain, after compiling objects
019      $(CC) $(OBJ) -o $@
021  xmk.d/obj/linux/$(SRCD)/%.o: $(SRCD)/%.c           # ugly and unwieldy copy/paste
022      mkdir -p $(@D)                                 # however could be remedied by placing lib/ in src/  
023      $(CC) $(CFLAGS) -MMD -MP -c $< -o $@           
024  xmk.d/obj/linux/$(LIBD)/%.o: $(LIBD)/%.c
025      mkdir -p $(@D)                                 # these create the necessary dirs to hold obj and dep files
026      $(CC) $(CFLAGS) -MMD -MP -c $< -o $@
028  .PHONY: clean linux
029  clean: $(OBJ) $(DEP)                               # getting rid of all the build files
030  rm $(OBJ) $(DEP)
032  linux: $(BIN)                                      # so the user can run the target by specifying the platform
034  -include $(DEP)                                    # include dependency files

Still this isn't a Makefile one would use in a real project. There's not even an option to create a debug build. I played around with a phony rule debug that would use ($eval....) to modify variables before running the build process, but it unfortunately didn't work out. The possible solutions I've come across to solve the remaining issues all don't sit well with me and are the main reason I was trying to develop my own build tool. That said, at this point it appears best to redevelop xmk as a build assistant around Make that either adds preprocessing to Makefiles or build Makefiles from config files that describe all the required rules and targets in a hierarchical way.
Right now I'm gravitating towards the latter option, especially since it would allow me to have one build tool for all sorts of programming environments.
So my conclusion remains that Make gets uncomfortable to deal with the moment you plan on using more than one or two simple target binaries in a Makefile.