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