Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Makefile compiling every time

Tags:

c

makefile

I just started to learn the C programming language in my University. We have a task to create a Makefile that uses the executable program (battle in my case) and compiles only when is necessary. My problem with it is that it compiles every time I use the make command and it does not show "Is up to date" as I want to. I have 2 objects to compile (quest.o and thorgrim.o and only quest.o has to be created since we have the thorgrim.o already) into "battle". Here's my code:

CC=gcc
EXE=battle
CFLAGS=-Wall

build: run
run: quest.o
     $(CC) $^ thorgrim.o -o $(EXE)
     ./$(EXE)
quest.o: quest.c
     $(CC) -c $^ -o $@
clean: rm -rf quest.o $(EXE)

What am I doing wrong? I want it to compile once and then show up to date if I won't touch the quest.c.

like image 719
Shavoks Avatar asked Sep 15 '25 06:09

Shavoks


2 Answers

Seeing as you're new at this, I'll walk you through it, so you can learn to do it right the first time. There's a lot of little details, that a lot of people end up missing, so hopefully this will give you a good reference:

First, lets start by defining your objects

OBJS := quest.o thorgrim.o

you can now create a pattern rule:

%.o: %.c
     $(CC) $(CFLAGS) -MD -MP $< -c -o $@

The first line of this rule %.o:%.c says that anything ending with a .o depends on a file with the same name ending in .c. Doing this prevents you from from having to write a separate rule for every object file. Notice that there is a default pattern rule to this effect, but we'll not go into that here.

Next, you call $(CC) with $(CFALGS). Notice that $@ now represents the name of the .o file you are generating, and $< represents the first dependency of the object (the .c file in this case).

The -MD and -MP are used to automatically generate dependency files -- for each .c file it will also create a .d file, which lists the dependencies (.h files) that your source requires to build. You have to -include the dependency files for it to work. (see https://gcc.gnu.org/onlinedocs/gcc-4.3.1/gcc/Preprocessor-Options.html for more details on exactly what these do). This way, if you modify a header file, it will automatically rebuild the necessary .o files. Having said that, you have to include those .d files in your makefile:

-include $(OBJS:.o=.d)

This will include the .d files as if someone cut and paste the code into your makefile. The $(OBJS:.o=.d) simply tells make to take the variable OBJS and replace all the .o's with .d's. Notice the - before the include. This tells make not to worry if the file doesn't exist (as will be the case on your first build ...).

Ok, now that you've got your .o files built, you need to build your program:

$(EXE): $(OBJS)
    $(CC) $^ -o $@

Add a default rule and comments, and you're done. So, in summary, your makefile will look something like:

CC:=gcc
EXE:=battle
CFLAGS:=-Wall
OBJS:=quest.o thorgrim.o

%.o: %.c
     $(CC) $(CFLAGS) -MD -MP $< -c -o $@

$(EXE): $(OBJS)
    $(CC) $^ -o $@

clean:
    rm -f $(OBJS) $(EXE)

-include $(OBJS:.o=.d)

EDIT: forgot the clean rule -- adding that.

like image 180
John Avatar answered Sep 16 '25 23:09

John


The run target depends on files, but the build rule does not produce a file named 'run'. A better idea is to do something along these lines:

run: $(EXE)
     ./$(EXE)

$(EXE): $(OBJ)
       $(CC) $(CFLAGS) -o $@ $+ 
like image 22
Bjorn A. Avatar answered Sep 17 '25 01:09

Bjorn A.