I'm doing the Learn C The Hard Way course by Zed Shaw, and on Lesson 2 I find that in the videos and book, the author only uses make ex1
to compile his C, while I need to run gcc -Wall -g ex1.c -o ex1
. Here is my Makefile, It seems like the first part is what is supposed to make the make command work to compile, but the "make" command doesn't compile it.
CFLAGs=-Wall -g
clean:
rm -f ex1.exe
all:
gcc -Wall -g ex1.c -o ex1.exe
The default goal is the first goal, so switch the order of these so that all
is the default.
There's more detail in the documentation on goals:
By default, the goal is the first target in the makefile (not counting targets that start with a period). Therefore, makefiles are usually written so that the first target is for compiling the entire program or programs they describe. If the first rule in the makefile has several targets, only the first target in the rule becomes the default goal, not the whole list. You can manage the selection of the default goal from within your makefile using the .DEFAULT_GOAL
variable
Your Makefile
has no dependency info included in the form of Rules. Rules are lines beginning at the left column (in column 0) and having a :
separator between targets (objects to be built) and requisites (objects provided to the rule)
Make does the chaining of rules so no target can be build before its prerequisites have, so a good starting point would be:
ex1.exe: ex1.o
this means that ex1.exe
(the first target in your Makefile
) depends on ex1.o
(the compiled object).... but how. You add the commands just below, but inserting in front of the command line a tab character:
[tab char]gcc -Wall -g ex1.o -o ex1.exe
First of all, see how the command takes ex1.o
as input for gcc
and generates (by means of option -o
) the file ex1.exe
.
The meaning of all of this is that always that you lack ex1.exe
or it happens to be modified (from its modification date in the file) before the prerequisite (ex1.o
) that command will be used.
Now, how we compile the object file? (in your Makefile it is made directly)
ex1.o: ex1.c
$(CC) $(CFLAGS) -c -o $@ $<
this line needs more explanation (although if you don't use it, it is automatically provided by the make
program) It uses substitution variables: $(CC)
expands to the default C compiler (cc
normally, but you can change it). $(CFLAGS)
is the variable you defined on top of your Makefile
and expands to the compiler options used normally to compile programs. $@
is expanded by the target file (in this case, the ex1.o
file) and the $<
expands to the left prerequisite of the right part of the rule. This rule, which is so common to compile C files into object files, is automatically generated for you by make
, and more can be created in make
advanced used (I'm not going to enter here, due to the amount of space needed) just to say that it can be written like this:
.c.o:
$(CC) $(CFLAGS) -c -o $@ $<
which means: any file with extension .o
can be created from a file with the same name, but extension .c
by using the next command line below. This makes automatically to depend each .o
file of a file with the same name but extension .c
and is preloaded in make
.
So, your simplest Makefile
could be:
CFLAGS=-Wall -g
src=ex1.c
ex1.exe: $(src)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(src)
as you see I've used a variable src
to describe the C source files used to compile my program. The dependency will be issued only when you have modified any or the prerequisites (there's only one now, on ex1.c
but could be more) and as I'm doing compiling and linking in the same compilation, I need to pass the compilation flags and the link flags (not used, you can delete the reference to $(LDFLAGS)
here) Indeed you could have written:
ex1.exe: ex1.c
cc -g -Wall -o ex1.exe ex1.c
clean:
rm -f ex1.exe
so, the default action is to create ex1.exe
, which depends on ex1.c
(so only when ex1.c
is modified, does ex1.exe
is being recompiled. To compile it, just do:
make ex1.exe
or
make
that will use the default first rule of the Makefile
.
clean
is a fake target (there's no file called clean
to generate) so when you use
make clean
its associated command is always executed. It makes make
to delete your target files (as it removes all the make generated files), so next time you run make
it will re-create all files by doing the necessary compilations.
For simple programs, let's say, several source .c
files, and some .h
files, with separate compilation of each (-c
option in compilation) and linking, I normally use this approach (definining the object files needed for each target program in a different variable)
# makefile for programs foo and bar.
targets = foo bar
TOCLEAN = $(TARGETS) # this variable stores what should be erased on clean target. Here we store the programs to erase.
foo_objs = a.o b.o c.o
TOCLEAN += $(foo_objs) # here, we add the objects of foo
foo: $(foo_objs)
$(CC) $(LDFLAGS) -o $@ $(foo_objs)
bar_objs = d.o e.o f.o
bar_libs = -lm -lintl -lX
TOCLEAN += $(bar_objs) # here we add the objects of bar
bar: $(bar_objs)
$(CC) $(LDFLAGS) -o $@ $(bar_objs) $(bar_libs)
clean:
rm -f $(TOCLEAN)
a.o b.o c.o: a.h b.h c.h globals.h
b.o c.o d.o e.o f.o: my_defs.h
(as you see, the dependency on the header files is to the compiled object --- not the .c
source file... the source file builds the dependency, but is the object file what has to be recreated in case the included file is modified, not the including .c
file)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With