Assume that the project has the following files, where the first files is dependent on the files under it's category in the unordered list:
- main.c
- global.h (contains enumerations and #define macros)
- sdlevent.h (generic header file)
- sdlevent.c (contains implementations of sdlevent.h)
- sdlshape.h (generic header file)
- sdlshape.c (contains implementations of sdlshape.h)
Now, I have the following makefile:
CC = gcc
CFLAGS = -g -w -std=c99
LIBS = -lSDL2 -lSDL2_image -lSDL2_ttf
SRCS = main.c sdlshape.c sdlevent.c
OBJS = $(SRCS:.c=.o)
EXE = play
all: $(EXE)
.c.o:
$(CC) -c $(CFLAGS) $<
$(EXE): $(OBJS)
$(CC) -o $(EXE) $(OBJS) $(LIBS)
$(OBJS): global.h sdlevent.h sdlshape.h
run : all
./$(EXE)
clean:
rm -f *.o *~ $(EXE)
The target
$(EXE)
is dependent on the target.c.o
. Do I absolutely need to define the target$(EXE)
after the target.c.o
?
I don't think so, because even though the target $(EXE)
is dependent on the target $(OBJS)
, the target ($EXE)
is declared before $(OBJS)
.
1 Answer 1
I have not divided up the header files into their .h and .c files because they are very small header files.
I would advice against this. Dividing into header and source files is just something you do. If something deserves an own source file, it also deserves an own header file. If nothing else, future readers of your code will expect .h files to only contain declarations. If you really don't want to split them up, then include the .c files instead. That's not good, but at least you're not hiding what you're doing.
Also, I don't really see the need to manually write dependencies. If your project is big, look into CMake or something. If it is small, use a generic Makefile. Here is a small example that basically contains everything you'll ever need before it's time to move on to CMake:
# Configuration
EXE = play
CFLAGS = -g -Wall -Wextra -std=c11
LDLIBS = -lm
LDFLAGS =
# Don't touch anything below
# Every .c is a source file (dough)
SRC = $(wildcard *.c)
# Every source file should be compiled to an object file
OBJ = $(SRC:.c=.o)
# Every object file should have a dependency file
DEP = $(OBJ:.o=.d)
# Link the exe from the object files
$(EXE): $(OBJ)
$(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS)
# Generate dependencies for the object files
%.o: %.c
$(CC) -MMD -MP -c $< -o $@
# https://stackoverflow.com/q/2145590/6699433
.PHONY: clean
clean:
$(RM) $(OBJ) $(DEP) $(EXE) *~
# Include the generated dependencies
-include $(DEP)
Note that you don't need to pass $(CFLAGS)
to the CC
commands. It's done automatically.
I used the above Makefile on this:
$ cat *.c *.h
/* bar.c */
#include "bar.h"
#include <math.h>
int bar(int a, int b) { return a*pow(b,3); }
/* foo.c */
#include "foo.h"
#include "bar.h"
int foo(int a, int b) { return a+bar(a,b); }
/* main.c */
#include "foo.h"
int main() { foo(1,2); }
/* bar.h */
#ifndef __BAR_H__
#define __BAR_H__
int bar(int a, int b);
#endif
/* foo.h */
#ifndef __FOO_H__
#define __FOO_H__
int foo(int, int);
#endif
I sacrificed a bit of readability to keep number of lines down. After running make
I have a couple of .d files.
$ cat *.d
bar.o: bar.c bar.h
bar.h:
foo.o: foo.c foo.h bar.h
foo.h:
bar.h:
main.o: main.c foo.h
foo.h:
-
\$\begingroup\$ I updated my question. Please check that. \$\endgroup\$3N4N– 3N4N2019年04月20日 17:16:03 +00:00Commented Apr 20, 2019 at 17:16
-
\$\begingroup\$ @klaus In this case it was ok, but please don't change the question when someone have answered it. It might invalidate an answer. You're not supposed to do continuous updates until you are satisfied. If needed, use the answers you've got and post a new question instead. \$\endgroup\$klutt– klutt2019年04月20日 17:20:05 +00:00Commented Apr 20, 2019 at 17:20
-
\$\begingroup\$ But the answer didn't associate with my question at all. Instead it suggested me another tool, which I thank you for anyway, and it just provided me with generic makefile instead of answering my question which was for learning purpose. So, the answer was invalid anyway. \$\endgroup\$3N4N– 3N4N2019年04月20日 17:21:40 +00:00Commented Apr 20, 2019 at 17:21
-
\$\begingroup\$ @klaus Then I misunderstood you. Sorry about that. In that case I suggest you undelete your question on SO, since it is more suitable there. You can leave this question open since it does make sense here. \$\endgroup\$klutt– klutt2019年04月20日 17:27:40 +00:00Commented Apr 20, 2019 at 17:27
-
\$\begingroup\$ regarding:
-include $(DEP)
This will result in the dependency files being rebuilt even if the parameter passed tomake
isclean
suggest:ifneq "$(MAKECMDGOALS)" "clean" -include $(DEP) endif
\$\endgroup\$user3629249– user36292492019年04月20日 17:30:52 +00:00Commented Apr 20, 2019 at 17:30
include
's the header file, then it checks withifndef
if the header file was included before anyway. I don't see any way the code can occur more than once. Maybe I'm not understanding the idea of module. Could you talk in respect of pure C, please and thanks? \$\endgroup\$bar.c
you will only need to generate a newbar.o
and then link it to a final executable. The filesmain.o
andfoo.o
can be reused without recompilation. \$\endgroup\$CFLAGS = -g -w -std=c99
Why are you turning OFF all the warnings with the-w
? Suggest using:CFLAGS = -ggdb -Wall -Wextra -Wconversion -pedantic -std=c99
And c99 is a (nearly) 20 year old version of C. Suggest using a recent version like:-std=c11
\$\endgroup\$