Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set up my makefile to compile C with just "make"

Tags:

c

makefile

cmder

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




like image 632
Jerry D Avatar asked Jan 09 '19 22:01

Jerry D


2 Answers

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
like image 116
tadman Avatar answered Oct 02 '22 04:10

tadman


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.

COMMON USE

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)

like image 24
Luis Colorado Avatar answered Oct 02 '22 03:10

Luis Colorado