4 ms·
Thank you for a good article. Even I learned some new stuff. However, there was a major omission: automatic dependency generation. Every now and then I forget h
by exDM69 14y ago
Thank you for a good article. Even I learned some new stuff. However, there was a major omission: automatic dependency generation. Every now and then I forget how it's done and try to find a good source on the net, but they're really scarce. On the other hand, there are tons of small "your first makefile" tutorials (this article is better than the average).
Here's my Makefile boilerplate. It build single executable from all .c files in this directory and scans for include file dependencies to properly cause recompilation when a header file is touched.
INCS=-I./
LIBS=-lglfw -lGLEW
CFLAGS=-std=gnu99 -g -ggdb -W -Wall -Wextra -pedantic
CFLAGS+=$(INCS)
CFLAGS+=-march=native -mno-80387 -mfpmath=sse -O3
SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o)
DEPS=$(SRCS:.c=.d)
EXECUTABLE=main
.PHONY: all clean
all: $(EXECUTABLE)
$(EXECUTABLE): $(OBJS)
$(CC) $(CFLAGS) $(LIBS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
%.d: %.c
$(CC) $(CFLAGS) -MM -o $@ $<
-include $(DEPS)
clean:
rm -f $(EXECUTABLE)
rm -f $(OBJS) $(DEPS)
If anyone has a better one (preferably with out-of-source build to put .d and .o files to a better place), please share it.
- thristian 14y agoI don't know about out-of-source building, but I know Make has a bunch of built-in rules that you can use to cut down your boilerplate. For example, I'm pretty sure that a filename depending on objects will automatically be created by linking those objects (although you'd have to specify your libraries in LDFLAGS rather than LIBS. Also, your rule for building .o files from .c files would be automatic if you put your include-directives in CPPFLAGS instead of INCS.
- endgame 14y agoYou should put something like this: ifneq ($(MAKECMDGOALS),clean) include $(DEPS) endif So you don't regenerate all the deps if you re-run clean.
- cygx 14y agoYou might want to provide separate CFLAGS and LDFLAGS. Also, I prefer adding -MMD to CFLAGS instead of having an additional rule for dependency generation: This way, you won't unnecessarily trigger dependency generation on targets which do not build objects like clean.
- mjschultz 14y ago(Sorry, I misread the above post, but I believe LDLIBS is the Makefile built-in variable for libraries to link.) Actually, in this case, I believe LDLIBS would be more appropriate since the OP isn't passing any flags to `ld` but rather the libraries for `ld` to link. I ended up getting hit be this in a simple Makefile a few weeks ago that worked in Fedora but failed in Ubuntu. The target was "%.o: %.c", which make auto-expands to be (something like): $(CC) $(CFLAGS) $(LDFLAGS) $@ $^ $(LDLIBS) But I had placed the libraries in LDFLAGS, instead of LDLIBS which caused Ubuntu to fail at finding a function in one of the libraries. Moving the libraries to LDLIBS solved the problem.
- cygx 14y agoI was more concerned about passing CFLAGS when linking - he already has a LIBS variable; you're right that the 'canonical' name for his LIBS variable is LDLIBS (as well as LOADLIBES - anyone an idea where that name came from?). EDIT: stop editing your answer while I'm writing my own ;) According to the manual, the built-in rule for linking an executable `n` from a single object file is $(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
- adam-a 14y agoI used this (on an admittedly unfinished and more or less abandoned project) https://github.com/adam-a/Simple-Sampler/blob/master/build/Makefile https://github.com/adam-a/Simple-Sampler/blob/master/build/M... I never found a good way to silo the .o and .d files though, the way I do it is put the main makefile in a separate dir from the tree so all the clutter can accumulate there.
- mccoyst 14y agoHere's a makefile: http://code.google.com/p/min-game/source/browse/makefile http://code.google.com/p/min-game/source/browse/makefile Here's the dependency script: http://code.google.com/p/min-game/source/browse/dep.sh http://code.google.com/p/min-game/source/browse/dep.sh Both inspired by code in "Recursive Make Considered Harmful", always worth a read: http://aegis.sourceforge.net/auug97.pdf http://aegis.sourceforge.net/auug97.pdf The built-in rules and variables for gnu make are worthless for anything but the most trivial project; don't bother with them. I'd be so happy if there was a makefile directive that forced make to run in "make -rR" mode.
- __david__ 14y agoA quick note: > INCS=-I./ This is generally a bad idea, unless you're explicitly trying to override #include <stdio.h> with a local file (and there are better ways to do that). <> are for system files, "" are for local files. So off the bat, your makefile example is better than most makefiles out there. I still see things that look like some dumb IDE wrote them with explicit .d and x.c: x.o rules littered about. Though I do note you aren't taking advantage of the built-in makefile rules (which are defined in POSIX), and you aren't taking advantage of gcc's -MMD flag, which does the dependencies during compilation instead of a separate step. That eliminates much of the file: LDLIBS=-lglfw -lGLEW CFLAGS+=-std=gnu99 -g -ggdb -W -Wall -Wextra -pedantic CFLAGS+=-MMD CFLAGS+=$(INCS) CFLAGS+=-march=native -mno-80387 -mfpmath=sse -O3 SRCS=$(wildcard *.c) OBJS=$(SRCS:.c=.o) EXECUTABLE=main .PHONY: all clean all: $(EXECUTABLE) $(EXECUTABLE): $(OBJS) $(CC) $(CFLAGS) $(LDLIBS) -o $@ $^ -include *.d clean: rm -f $(EXECUTABLE) rm -f $(OBJS) *.d Furthermore, I like to name my objects explicitly instead of using * .c, which lets you order them on the command line, if you find that makes a difference. In particular, if your executable is named the same as one of your C source files then you don't even need the link line, as long as the C source file is the first dependency: LDLIBS=-lglfw -lGLEW CFLAGS+=-std=gnu99 -g -ggdb -W -Wall -Wextra -pedantic CFLAGS+=-MMD CFLAGS+=$(INCS) CFLAGS+=-march=native -mno-80387 -mfpmath=sse -O3 OBJS=main.o obj1.o obj2.o .PHONY: all clean all: main main: $(OBJS) -include *.d clean: rm -f $(EXECUTABLE) rm -f $(OBJS) *.d I tend to like to prefix variables with the thing you are making so that different executables in the same Makefile can have (drastically) different compilation parameters: .PHONY: all clean all: main main: LDLIBS=-lglfw -lGLEW main: CFLAGS+=-std=gnu99 -g -ggdb -W -Wall -Wextra -pedantic main: CFLAGS+=-MMD main: CFLAGS+=$(INCS) main: CFLAGS+=-march=native -mno-80387 -mfpmath=sse -O3 main: OBJS=main.o obj1.o obj2.o main: $(OBJS) -include *.d clean: rm -f $(EXECUTABLE) rm -f $(OBJS) *.d If you have an embedded project that needs both compiling and cross-compiling that trick can work wonders.