1
\$\begingroup\$

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).

asked Apr 20, 2019 at 15:43
\$\endgroup\$
7
  • 1
    \$\begingroup\$ I don't like the idea behind 'in the future if I divide the header files into their respective .c files'. The .h files are generally for publishing declarations of objects - global variables and routines (and types, constants etc.), .c files are for their implementation (initializers and code, respectively). What you said implies you have some code in .h file. That's rather bad, because the implementation will appear in every module which includes such .h file. It doesn't seem to hurt as long as you have just one .c module, but better fix it asap. \$\endgroup\$ Commented Apr 20, 2019 at 16:46
  • \$\begingroup\$ @CiaPan, Could you explain "the implementation will appear in every module which includes such .h file"? Because if the module include's the header file, then it checks with ifndef 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\$ Commented Apr 20, 2019 at 17:19
  • \$\begingroup\$ @klaus One point of dividing stuff up is to only recompile what's needed. In the example in my answer, if you do a change to bar.c you will only need to generate a new bar.o and then link it to a final executable. The files main.o and foo.o can be reused without recompilation. \$\endgroup\$ Commented Apr 20, 2019 at 17:23
  • 1
    \$\begingroup\$ regarding: 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\$ Commented Apr 20, 2019 at 17:24
  • \$\begingroup\$ @user3629249, thanks. I know I'm using old version of C and turning off warnings. I'm not building useful stuff. This is all for learning purposes. And the c99 standard forces you to know a lot of intricate stuff. \$\endgroup\$ Commented Apr 20, 2019 at 17:26

1 Answer 1

2
\$\begingroup\$

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:
answered Apr 20, 2019 at 16:58
\$\endgroup\$
9
  • \$\begingroup\$ I updated my question. Please check that. \$\endgroup\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented Apr 20, 2019 at 17:27
  • \$\begingroup\$ regarding: -include $(DEP) This will result in the dependency files being rebuilt even if the parameter passed to make is clean suggest: ifneq "$(MAKECMDGOALS)" "clean" -include $(DEP) endif \$\endgroup\$ Commented Apr 20, 2019 at 17:30

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.